Skip to content

feat(doctor): judge a solution's permissions, and report a shell with no app - #1221

Merged
antoinetoussaint-byte merged 2 commits into
mainfrom
solution-permissions-and-shell
Oct 28, 2026
Merged

antoinetoussaint-byte merged 2 commits into
mainfrom
solution-permissions-and-shell

Conversation

@antoinetoussaint-byte

Copy link
Copy Markdown
Contributor

Follow-up to #1218. Two checks, both found by running the tool against the real fleet rather than by design.

1. Permissions — Core already catches the class; nothing was running it

The missing-permission bug — a role promising authority nothing backs — is caught by Core today. Reading the contract through modulecontract.Load refuses:

role "installer" grants documents:read, which is neither within the module's own scope
ceilings nor a scope one of its bindings spells out for invoke or lookup

Nothing was running that over solutions, so it surfaced at the first refused call instead of before install. doctor solution runs it now, and TestARoleGrantingAnUnbackedScopeIsReported pins the message. This is the payoff of reading every document through the reader that owns it rather than parsing YAML here: all 69 of Core's contract rules come along.

Adds consumed_module_without_binding as a warning — a module in api.consumes the contract declares no binding for. On wiki it names robin, annotations, tracing, and wiki's own contract says why that is right:

Robin, tracing and annotations are reached at the edge with the person's own Work Context, so the wiki asks no binding at any of them.

Correct there, a bug elsewhere, and the two are indistinguishable from these documents — so it is surfaced, not failed.

A check I wrote and deleted

The first draft failed any binding no role named by id. It reported wiki and proposals — the two longest-running solutions — as broken. A role also reaches another module's authority by declaring that module's resource kinds, with no RoleBinding at all, which is exactly how both do it:

- name: viewer
  scopes:
    - resource_kind: documents     # the TARGET module's kind
      actions: [read]

The check is gone and the source records why, including what cannot be answered from a directory at all: the authoritative version needs Contract.Resolve(values) against a composition's configuration, which is a composition-scoped check and a separate piece of work.

2. page_shell_has_no_app

Conforming the fleet to one shape added the web shell's manifest to solutions whose page had never been written — writing one would have been inventing product in a conforming change. On main today lastlogin-go and robin-nest are in that state: correctly shaped, with codefly run service web having nothing to start.

A declared service with no source is invisible to every other reading. Each manifest is valid, every gate passes, and the first sign is a run that does nothing. A warning, because the solution is not wrong and nothing breaks until someone runs the shell.

Verified against main: wiki, proposals and robin-builder ship real shells and are silent; lastlogin-go and robin-nest report it.

Verification

wiki OK   proposals OK   lastlogin-go OK   robin-builder OK   robin-nest OK
releases FAIL   excel-extract FAIL   lastlogin-python FAIL   summary FAIL

Five of nine conform on core-solutions main, up from two when this started. The four failures each have a PR in flight.

go build ./..., go test ./pkg/solutionshape ./cmd pass, golangci-lint 0 issues, docs/cli-reference.md regenerated.

What I did not verify

Nothing was booted or rendered. The composition-resolved half of permissions — what a binding's audience and resource-kind slots resolve to under a real composition — is explicitly not judged, and the source says so where someone will look for it.

🤖 Generated with Claude Code

antoinetoussaint-byte and others added 2 commits October 28, 2026 16:50
… them

The missing-permission class — a role promising authority nothing backs —
is already caught by core: reading the contract through modulecontract
.Load refuses "role installer grants documents:read, which is neither
within the module's own scope ceilings nor a scope one of its bindings
spells out". Nothing was running that over solutions, so it was found at
the first refused call instead of before install. It runs here now, and
a test pins the message.

Adds consumed_module_without_binding as a WARNING: a module in
api.consumes the contract declares no binding for. Correct when the
module is reached with the viewer's own credential — wiki reaches robin,
tracing and annotations that way and says so — and a bug when the
solution calls it on a person's behalf. The two are indistinguishable
from these documents, so it is surfaced rather than failed.

A first draft also failed any binding no role named by id. It reported
wiki and proposals — the two longest-running solutions — as broken,
which is how the second mechanism was found: a role reaches another
module's authority by declaring that module's resource KINDS, with no
RoleBinding at all. That check is gone, and the source says what cannot
be answered from a directory: the authoritative version needs
Contract.Resolve(values) against a composition.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Conforming the fleet to one shape added the `web` shell's MANIFEST to
solutions whose page had never been written — writing one would have been
inventing product in a conforming change. lastlogin-go and robin-nest are
in that state on main today: correctly shaped, and `codefly run service
web` has nothing to start.

A declared service with no source is invisible to every other reading.
Each manifest is valid, every gate passes, and the first sign is a run
that does nothing. So it is reported — as a warning, because the solution
is not wrong and nothing breaks until someone runs the shell.

wiki, proposals and robin-builder ship real shells and are silent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@antoinetoussaint-byte
antoinetoussaint-byte added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit f7bfa66 Oct 28, 2026
19 checks passed
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