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
10 changes: 9 additions & 1 deletion .claude/skills/api-boundary-triage/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ contract moves, so it cannot be.
Run it alone while working:

```bash
go test ./internal/apiboundary/ -v
go test -count=1 ./internal/apiboundary/ -v
```

`-count=1` is not optional while you edit a facade. The gate loads the module
through `go list`, whose reads Go's test cache does not track, so a cached run
reports the tree as it was before your change.

Two tests. `TestPublicAPINamesEveryTypeItExposes` runs the rule over this
module. `TestGateCatchesKnownBypasses` runs it over `testdata/fixture`, a
module built to break it in every way an earlier syntactic version could be
Expand All @@ -36,6 +40,10 @@ puts the type in front of a consumer.
type alias carries the type and *not* the package-level constants declared
with it, so a consumer can hold the value and cannot write the comparison.
Fix: re-export the constants too.
- **a path ending `oneof <Wrapper>`** — a oneof field's type is an unexported
interface nothing can alias; the gate walks the exported wrappers that
implement it instead, because those are what a consumer writes
(`&accounts.AddSourceRequest_Api{...}`). Fix: alias the wrapper.
- **`no public packages discovered`** — not a boundary violation. The loader
found nothing, which means the module failed to load. Fix the build first.
- **a `load <pkg>` error** — same: the gate refuses to report a vacuous pass.
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/refresh-generated-api/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ re-exports in that package's `types.go`, and generated enums need their
constants too. `internal/apiboundary` decides this, not judgement:

```bash
go test ./internal/apiboundary/ -v
go test -count=1 ./internal/apiboundary/ -v
```

Use the `api-boundary-triage` skill for a failing run — in particular for which
Expand Down
11 changes: 10 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,18 @@ jobs:
uses: ./sdk/.github/actions/host-source
with:
gateway: "true"
# The build is the host's gateway module with this SDK required into it,
# so it needs the newer of the two modules' `go` directives; setup-go
# pins GOTOOLCHAIN=local, so neither is fetched on demand.
- id: go
run: |
set -euo pipefail
sdk=$(awk '$1 == "go" { print $2; exit }' sdk/go.mod)
host=$(awk '$1 == "go" { print $2; exit }' "source/${{ steps.source.outputs.gateway }}/go.mod")
echo "version=$(printf '%s\n%s\n' "$sdk" "$host" | sort -V | tail -n1)" >> "$GITHUB_OUTPUT"
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: sdk/go.mod
go-version: ${{ steps.go.outputs.version }}
cache-dependency-path: |
sdk/go.sum
source/${{ steps.source.outputs.gateway }}/go.sum
Expand Down
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,15 @@ mints through the host's *real* gateway handlers at that same ref

| Path | Owns |
| --- | --- |
| `accounts/` | gateway-bound facade over the accounts services; `types.go` holds its alias re-exports |
| `datasource/` | the same facade over `DatasourceService` |
| `accounts/` | gateway-bound facade over the accounts services (`Audit()`, `Datasources()`: the raw contract); `types.go` holds its alias re-exports |
| `datasource/` | the ergonomic, effect-safe facade over `DatasourceService` |
| `settings/` | presence-aware `Field[M, T]` runtime; `JSONCodec` is the only typed↔ProtoJSON boundary |
| `settings/catalog/` | Go / TypeScript / proto catalog renderers that `module-compose` calls |
| `moduleauthority/` | a module's own authority: gateway broker mints; direct Accounts authority or explicitly selected gateway authority transport. Work Contexts are opaque `string`s, shape-checked only by `core/workcontext.Decode`. `verifier_sources.go` serves core's three live-authority sources to a callee's verifier without verifying anything itself |
| `gen/` | generated output. Regenerated, never edited |
| `conformance/hostgateway/` | `moduleauthority` minting through the host's real gateway at the `SOURCE.txt` ref |
| `internal/apiboundary` | the public-API boundary gate, with fixtures that prove it still looks |
| `internal/connectresponse` | the one HTTP client both `DatasourceService` facades read responses through |
| `internal/agentcontext` | the budget and frontmatter contract for this file and the skills |

`README.md` covers what each surface does for a *consumer*, with worked
Expand Down
92 changes: 92 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,98 @@

## Unreleased

Core main and module-saas-starter v0.0.103. No Core tag carries the Core this
needs, so the floor is a pseudo-version; it moves to the first tag that does.

### Changed

- **Core floor: `v0.19.6-0.20261018051341-b81d92e6feda`** (Core main,
2026-10-18), from v0.19.4 on this branch and v0.19.1 in v0.1.3. Core
`22ddde98` removed the approved build from a Work Context: `SealSource` lost
`ApprovedBuild` and `ErrNoApprovedBuild` is gone, so v0.1.3 does not compile
for any consumer that requires that Core. It is also the Core the v0.0.103
gateway requires. Core v0.19.5 still requires `SealSource.ApprovedBuild` and
cannot build this release; MVS never selects it beside this floor.
- **`gen/` moves from `9ab36e7e` to released module-saas-starter v0.0.103**
(`2cb00737`), with the existing plugin pins and buf v1.73.0. The tree
reproduces byte-for-byte from that tag, and `SOURCE.txt` records it for both
`gen-drift` and `conformance/hostgateway`. The module package version stays
0.1.0; regeneration cuts no SDK release.
- **The module broker is the gateway's workload endpoint.** Host `af5cef9a`
(module-saas-starter#1120) serves the three mints there alone — the public
endpoint answers 404 — and `Seams.Gateway`'s documentation now says so. The
same commit dropped the internal token from the work-context mint and relays
accounts' `Unauthenticated` there as 401 (`ErrInvalidCredentials`) instead of
502. The client is unchanged: it still sends the internal token on every
mint, which the two operation mints still require. `conformance/hostgateway`
serves the real gateway through the host's own `workloadListener`, and
`moduleauthority`'s fake gateway follows the new perimeter.
- `datasource`'s response-encoding client moves to `internal/connectresponse`
unchanged, so `accounts.Datasources()` reads responses through it too.

### Removed (breaking)

- **`(*moduleauthority.WorkContextSources).ApprovedBuild`**. Core's verifier
no longer asks an issuer for an approved build — a change of approved builds
revokes through the principal's epoch and the installation's revision — and
its only correct "bears no execution" answer was the sentinel Core deleted.
There is no replacement; a consumer calling it directly drops the call.
`WorkContextSources` still implements Core's `SealSource`, `RevisionSource`
and `GrantSource`.
- Generated, by the v0.0.103 regeneration, against v0.1.3:
`ModuleMintWorkContextRequest.Upstream` and `.Manifest` (with their getters;
the host stopped accepting a reported upstream and manifest), and
`SolutionBackendBinding`, `SolutionRegistration.Backend`,
`SolutionFrontendBinding.ContractVersion` and
`SolutionRegistrationStatus_SOLUTION_REGISTRATION_STATUS_INCOMPATIBLE`, all
in host `af5cef9a` (module-saas-starter#1120). No
hand-written facade exposed any of them, and `moduleauthority` never sent
`Upstream` or `Manifest`.
- Generated interfaces gain methods, which breaks a consumer that implements
them: `DatasourceServiceClient`/`Handler.UpdateSource` and
`SolutionRegistryServiceClient`/`Handler.GetRetainedSolutionFrontend`.
Embedding the generated `Unimplemented…Handler` absorbs the handler half.

### Added

- **`accounts.Client.Datasources()`**, the `DatasourceService` facade beside
`Audit()`: `ListSources`, `GetSource`, `AddSource`, `ListSourceOperations`,
`DeclareSourceOperations`, `InvokeSourceOperation`,
`LookupInvokeSourceOperation`, `BeginDatasourceAccountLink`,
`CompleteDatasourceAccountLink` and `ListMyDatasourceAccountLinks`, bare
messages in and out, the host's Connect error returned intact. Every message,
enum, enum value and oneof wrapper they reach is re-exported from `accounts`
under its generated name (`AddSourceRequest_Api`,
`ApiOAuth2Config_GRANT_AUTHORIZATION_CODE`, …), so connecting and calling a
host-owned API source no longer needs `gen/`. It is the raw contract;
`datasource`'s `Invoke`/`Lookup` remain the effect-safe way to call an
operation.
- `moduleauthority.Client.MintModuleWorkContext(ctx)` returns
`ModuleContext{Token, ExpiresAt}` without caching, coalescing or retrying.
`ExpiresAt` is the broker's reported expiry, not a decoded token claim.
Existing `ModuleWorkContext(ctx) (string, error)` retains its cache and
shared refresh through the same mint implementation; organization binding,
bounded reads, Core-backed sealed-shape checking and safe refusals are shared.
- `BrokerTransportConnectionReset` classifies wrapped `syscall.ECONNRESET`
through error identity with a fixed diagnostic, retaining no underlying
cause. It is appended so existing transport category values remain stable;
callers can choose to retry without accessing sensitive transport errors.
- Generated, by the v0.0.103 regeneration: `UpdateSource` (a connected source
edited in place), `GetRetainedSolutionFrontend`, installation readiness
(`InstallationReadiness`, `ReadinessCondition` and their enums), carried
module access on team grants, `SolutionGroup`, `SourceFetchScope` and
`CheckAuthorizationRevisionRequest.SealsNoBinding`. These add no
hand-written facades.
- `internal/apiboundary` walks a oneof field's exported wrappers. The field's
type is an unexported interface nothing can alias; the gate used to report
that interface and stop, and now requires each wrapper a consumer writes to
be re-exported (fixture `oneof`).

## v0.1.3 — served verifier sources

Tagged at `14fe024` (#44) without a heading here; these are the entries it
shipped.

### Added

- **`moduleauthority.WorkContextSources`**, the three live-authority sources a
Expand Down
Loading
Loading