Skip to content

Hand a solution page its viewer organization's installation - #1036

Merged
antoinetoussaint-byte merged 2 commits into
mainfrom
solution-page-installation
Oct 7, 2026
Merged

antoinetoussaint-byte merged 2 commits into
mainfrom
solution-page-installation

Conversation

@antoinetoussaint-byte

Copy link
Copy Markdown
Contributor

A solution page now receives its viewer organization's installation from the host, as an optional SolutionBinding.installationId.

Why

An agent module binds every assistant turn to an (installation, intent) pair that it verifies against the host. The host handed a solution page only solutionId, apiBase and the token accessors, and an org member has no read from (organization, solution) to installation. So a consuming solution could only run that module's assistants by asking a person to paste an installation UUID. That is self-identification, and a page-chosen installation could be another organization's.

The data was already there. SolutionEntitlementService.ListSolutionEntitlements returns installation_id for every entitled installation; the gateway's /solutions/_entitlements projection dropped it. Agreed with the #953 author: resolve it per (viewer organization, routed target), never by alias, never from the page, on main after #953.

What changes

  • auth-gateway: solutionEntitlementEntry carries installationId, copied from the authority answer.
  • frontend: SolutionEntitlement.installationId is parsed (empty when an older gateway does not say) and is part of the projection cache revision.
  • New projection GET /api/solutions/[id]/installation: verified entitlements → the navigation's own entitledSolutions join over the whole registry → {installationId, healthy}, or 404 for unregistered and unentitled alike. Registered in the boundary test's projectionSurfaces, so it must keep narrowing through the entitlement read.
  • SolutionOutlet reads it with authedFetch and passes installationId to the remote. Undefined while resolving or on any refusal; it never substitutes a value.
  • saas-ui SolutionBinding.installationId?: string, optional per the binding's own rule (a minor of the host↔remote contract).

Classification: fix at the owner (the host decides which installation a viewer acts in). Docs: module/SOLUTION_REGISTRATION.md (§4, the third projection) and services/frontend/code/packages/saas-ui/README.md.

Verified

  • auth-gateway: go vet ./... and go test ./... pass; the entitlement test asserts the id travels.
  • frontend: tsc --noEmit clean; the solutions, api/solutions and saas-ui solution suites pass (382 tests), including 5 new route tests (entitled, unentitled, unregistered, older gateway, unauthenticated) and 2 outlet tests (the id reaches the page; a refusal hands none).
  • module/tools (boundary and doc-claim gates) passes.

Not verified

🤖 Generated with Claude Code

antoinetoussaint-byte and others added 2 commits October 7, 2026 15:16
…llation

The authority's entitlement answer already carried each installation id,
and the gateway dropped it, so a solution page could not learn which
installation it acts in. An agent module binds every assistant turn to
one, so a consuming solution had to ask a person to paste a UUID.

The gateway's /solutions/_entitlements entries now carry installationId.
GET /api/solutions/<id>/installation resolves the verified viewer
organization's active installation of the routed solution through the
navigation's own entitledSolutions join (404 when unregistered or not
entitled), the outlet reads it with the viewer's session, and
SolutionBinding gains an optional installationId. A page never names one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p test names generic

SolutionBinding gained installationId, so saas-ui publishes changed bytes:
the kit moves to 0.12.6 with @codefly-dev/ui and the frontend pins. The
route test named real solutions, which the boundary gate refuses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@antoinetoussaint-byte
antoinetoussaint-byte added this pull request to the merge queue Oct 7, 2026
Merged via the queue into main with commit b24a2e1 Oct 7, 2026
24 checks passed
antoinetoussaint-byte added a commit that referenced this pull request Oct 7, 2026
…1013)

main moved five commits under this branch — #953 (the solution/module lifecycle),
#1029, #1034, #1036 and release v0.0.93 — which left the PR unmergeable. An
unmergeable PR schedules NO checks, so "0 failing checks" was zero checks rather
than a green run; nothing had graded the last four commits.

Three things the merge had to settle:

- **A migration number collision.** #953 added migrations 20 through 29, and this
  branch had taken 20. Two files at one version refuse the whole lineage at
  runtime-init ("migration version 20 has two down files"), which is why the
  dependency boot failed rather than any test. Renumbered to 30; every version is
  now unique. Migration numbers are renumbered, never reused.
- **pkg/business/service_test.go** — main wired the policy log into the test
  service beside the cipher wiring this branch replaced. Both belong, so both are
  kept.
- **pkg/infra/export_test.go** — main added ExecAsControlPlane and this branch
  added the enveloped-column bridge, at the same place and independent of each
  other. Both kept.

The generated control-plane call-site allowlist was REGENERATED rather than
trusted as a text merge: it auto-merged cleanly, which proves nothing about
whether it still matches the code on both sides.

**Not verified, and unverified is not working:** the database-backed suites did
not run after the merge. The local Postgres lineage is dirty at version 23 — one
of main's new migrations, left mid-apply by the boot that failed on the number
collision — and the postgres agents serving it are owned by other live sessions
on this machine, so resetting it would disturb work that is not mine. Build,
untagged `go vet ./...`, the pure suites, the keyservice suite, the accounts root
suite and the repository gates are green; the DB suites are CI's to grade, on a
clean database.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@antoinetoussaint-byte
antoinetoussaint-byte deleted the solution-page-installation branch October 25, 2026 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant