feat(family): platforms declared at inception and detected from bound repos (milestone 463 step 2, #4988)
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 21s
CI & Build / TypeScript typecheck (push) Successful in 1m10s
CI & Build / integration (push) Successful in 1m48s
CI & Build / Python tests (push) Successful in 2m40s
CI & Build / Build & push image (push) Failing after 44s

A project's platforms decide which family ideas reach it. This step makes membership answerable from every door:

- services/platforms.py: the global catalog (writes are admin-only and duplicate-gated by slug); pure marker detection; and membership reads and writes. Detection only ADDS, and only where nobody has answered. It never overrides a declared or rejected row and never removes one.
- coverage: the archive scan now carries every path, and the refresh runs detection fail-open.
- inception: a platforms choice (slugs, or null for unanswered). The list is the whole answer: members left out of it become rejected.
- MCP: list_platforms and set_project_platforms; enter_project and get_project carry the project's platforms.
- REST: /api/platforms (admin writes) and /api/projects/<id>/platforms.
- UI: a platforms checklist on the inception card, a Family tab on ProjectView, and a Platforms admin tab in Settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 09:51:28 -04:00
co-authored by Claude Opus 5.5
parent e68ccc5884
commit 07d2542479
27 changed files with 1638 additions and 51 deletions
+28
View File
@@ -0,0 +1,28 @@
"""Structural tests for the platforms blueprint (milestone 463 step 2) —
registration and the route-to-service contract. What the writes do is in
tests/test_integration_platforms.py."""
import inspect
def test_platforms_blueprint_registered_in_app():
from scribe.app import create_app
app = create_app()
assert "platforms" in app.blueprints
rules = {(r.rule, m) for r in app.url_map.iter_rules() for m in r.methods}
for rule, method in (
("/api/platforms", "GET"),
("/api/platforms", "POST"),
("/api/platforms/<int:platform_id>", "PATCH"),
("/api/projects/<int:project_id>/platforms", "GET"),
("/api/projects/<int:project_id>/platforms", "PUT"),
):
assert (rule, method) in rules, f"{method} {rule} is not routed"
def test_writes_to_the_catalog_and_to_a_project_take_the_caller():
"""The catalog's admin gate and the project's write gate both live in the
service, so every write must be handed the caller to check."""
from scribe.services import platforms as svc
for name in ("create_platform", "update_platform", "set_project_platforms",
"project_platforms"):
assert "user_id" in inspect.signature(getattr(svc, name)).parameters