diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cd4d041..6368910 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -5,30 +5,6 @@ on:
pull_request:
jobs:
- build-test-docs:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 10.0.x
- - uses: actions/setup-python@v5
- with:
- python-version: '3.12'
- - run: dotnet restore TypeFerence.slnx
- - run: dotnet build TypeFerence.slnx --no-restore --configuration Release
- - run: dotnet test TypeFerence.slnx --no-build --configuration Release
- - run: dotnet src/TypeFerence.Cli/bin/Release/net10.0/typeference.dll build examples/helio --target all --out dist-ci-1 --emit-ard --publisher-domain helio.example
- - run: dotnet src/TypeFerence.Cli/bin/Release/net10.0/typeference.dll build examples/helio --target all --out dist-ci-2 --emit-ard --publisher-domain helio.example
- - run: diff -qr dist-ci-1 dist-ci-2
- - run: pip install -r tools/requirements-docs.txt
- - run: python tools/render_whitepaper.py
- - run: cp output/pdf/typeference-whitepaper.pdf /tmp/typeference-whitepaper-first.pdf
- - run: python tools/render_whitepaper.py
- - run: cmp /tmp/typeference-whitepaper-first.pdf output/pdf/typeference-whitepaper.pdf
- - run: sudo apt-get update && sudo apt-get install -y poppler-utils
- - run: pdfinfo output/pdf/typeference-whitepaper.pdf
-
go-implementation:
runs-on: ubuntu-latest
steps:
@@ -44,8 +20,10 @@ jobs:
working-directory: go
- run: go test ./...
working-directory: go
- - name: Static binary (CGO_ENABLED=0)
- run: CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /tmp/typeference ./cmd/typeference
+ - name: Static binaries (CGO_ENABLED=0)
+ run: |
+ CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /tmp/typeference ./cmd/typeference
+ CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /tmp/typeference-lsp ./cmd/typeference-lsp
working-directory: go
- name: Go binary reproduces committed reference output byte-for-byte
run: /tmp/typeference diff examples/helio --against dist --emit-ard --publisher-domain helio.example
@@ -56,31 +34,21 @@ jobs:
GOOS=js GOARCH=wasm go build ./cmd/typeference-wasm
go run ./cmd/playground-pack -root .. -out /tmp/examples.json
working-directory: go
- # NOTE: BETH pack/score is intentionally NOT gating CI yet. The harness is
- # unproven end to end (no real judged run has been collected), and an
- # offline pack+score observes nothing — a green scorecard there would be
- # vacuous (see ADR-0009 and score.go's judged-coverage requirement). Wire
- # it back in once a real run is exercisable in CI or on the dev machine.
- # Cross-implementation conformance: both compilers run the shared fixture
- # corpus in conformance/; a digest mismatch on any fixture is a broken
- # build. See conformance/README.md and ADR-0005.
- conformance:
+ # Determinism suite: the Go compiler compiles the shared fixture corpus in
+ # conformance/ and must reproduce the committed digests. This is the
+ # single-implementation successor to the cross-implementation suite; the
+ # fixtures and canonicalization rulings (ADR-0004) are unchanged (ADR-0014).
+ determinism:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 10.0.x
- uses: actions/setup-go@v5
with:
go-version: stable
cache-dependency-path: go/go.sum
- - name: Conformance suite (Go implementation)
- run: go test ./conformance -v
+ - run: go test ./conformance -v
working-directory: go
- - name: Conformance suite (C# reference implementation)
- run: dotnet test TypeFerence.slnx --filter FullyQualifiedName~ConformanceSuiteTests
# The repository-root AGENTS.md and dist-maintainer are build artifacts of
# agents/maintainer (the self-hosted maintainer definition). Drift between
@@ -98,3 +66,18 @@ jobs:
working-directory: go
- name: Root AGENTS.md is the compiled artifact
run: cmp AGENTS.md dist-maintainer/neutral/typeference-maintainer/AGENTS.md
+
+ docs:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.12'
+ - run: pip install -r tools/requirements-docs.txt
+ - run: python tools/render_whitepaper.py
+ - run: cp output/pdf/typeference-whitepaper.pdf /tmp/typeference-whitepaper-first.pdf
+ - run: python tools/render_whitepaper.py
+ - run: cmp /tmp/typeference-whitepaper-first.pdf output/pdf/typeference-whitepaper.pdf
+ - run: sudo apt-get update && sudo apt-get install -y poppler-utils
+ - run: pdfinfo output/pdf/typeference-whitepaper.pdf
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 3131db8..0a4d1e4 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -15,20 +15,16 @@ permissions:
contents: write
jobs:
- # Nothing ships unless both implementations, the shared conformance suite,
- # and the self-host drift gate are green at the tagged commit.
+ # Nothing ships unless the test suite, the determinism suite, and the
+ # self-host drift gate are green at the tagged commit.
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 10.0.x
- uses: actions/setup-go@v5
with:
go-version: stable
cache-dependency-path: go/go.sum
- - run: dotnet test TypeFerence.slnx
- run: go test ./...
working-directory: go
- name: Self-host drift gate
diff --git a/.gitignore b/.gitignore
index 2e3fde8..9a70256 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,8 @@ __pycache__/
web/playground/typeference.wasm
web/playground/wasm_exec.js
web/playground/examples.json
+
+# Codex remote attachment scratch
+.codex-remote-attachments/
+go/typeference-wasm
+*.wasm
diff --git a/AGENTS.md b/AGENTS.md
index 44fb18d..88c5e40 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -8,7 +8,7 @@ The agent that maintains the TypeFerence repository, defined in TypeFerence's ow
- Where the specification and an implementation disagree, the specification wins; record the ruling in docs/decisions and cover it with a conformance fixture.
- Every canonicalization or composition ruling ships with a fixture under conformance/fixtures in the same change.
- Never silently diverge from the specification; fixing the specification is allowed, silent divergence is not.
-- Any change to code generation must keep the C# and Go implementations byte-identical, verified by the conformance suite before merge.
+- Any change to code generation must reproduce the committed digests, verified by the determinism suite before merge.
- Never weaken determinism, provenance, or fail-closed behavior to make a change easier.
- A conformance digest is regenerated only together with the specification change and ADR that justify it; never hand-edit a digest.
- Repeated builds from identical source must stay byte-identical on every platform.
@@ -16,17 +16,115 @@ The agent that maintains the TypeFerence repository, defined in TypeFerence's ow
- signatureIntent.required fails closed; the unsigned-staging option exists solely to emit payloads for an external signer.
- TypeFerence imports externally produced signatures; it never signs, never verifies cryptographic validity, and never resolves keys.
- Trust metadata is declarative; never dereference identity, attestation, or provenance URIs during compilation.
-- Every commit builds and passes both implementations' test suites and the shared conformance suite.
+- Every commit builds and passes the test suite and the determinism suite.
- Decisions with real tradeoffs are recorded as ADRs in docs/decisions before the change merges.
- Documentation must be accurate against the code at the commit that includes it; no fabricated adoption, benchmarks, or endorsements.
- Commit messages are conventional and written for a critical human reader.
## Context slots
-- `repositoryMap`: `context/repository-map.md`
+- `repositoryMap`: `typeference/context/repository-map@0.1.0`
+
+## Context
+
+### Contribution Workflow
+
+# Contribution workflow
+
+- Work on feature branches; never rewrite published history; never tag or publish a
+ release outside the checklist in `docs/release-checklist.md`.
+- Before any commit: `go test ./...` (from `go/`) and the determinism suite
+ (`make conformance`) both pass. A commit that breaks either does not land.
+- Design decisions with real tradeoffs — spec semantics, canonical bytes, trust
+ model, dependencies — are recorded in `docs/decisions/` as numbered ADRs in the
+ same change.
+- Generated artifacts (root `AGENTS.md`, `dist/`) are only ever changed by
+ regenerating them from source (`make selfhost`, `typeference build`); hand edits
+ to generated files are drift and CI rejects them.
+- Documentation is accurate against the code at the commit that includes it. The
+ project describes itself as an experimental reference implementation; no invented
+ adoption, users, benchmarks, or endorsements, ever.
+
+### Determinism Guarantees
+
+# Determinism guarantees
+
+Identical source must compile to identical bytes — across repeated builds and
+across platforms. The guarantee is enforced, not aspirational:
+
+- `conformance/fixtures/` records the expected `typeference-directory-v1` digest of
+ every emitted target for the fixture corpus; the compiler must reproduce them in
+ CI (a golden-file determinism suite).
+- The committed `dist/` tree is the fully materialized reference output for
+ `examples/helio`; the compiler byte-compares against it in its tests.
+- The committed root `AGENTS.md` and `dist-maintainer/` are build artifacts of
+ `agents/maintainer/`; CI recompiles the definition and fails on any drift.
+
+Rules that protect the guarantee:
+
+- Digest values are regenerated (`go test ./conformance -update`), never typed by
+ hand.
+- Canonical serialization is defined in `docs/specification.md` ("Canonicalization");
+ any change to it is a specification change with an ADR.
+- Nothing about determinism, provenance, or fail-closed behavior is ever relaxed to
+ make an unrelated change easier. If a change fights the determinism rules, the
+ change is wrong or the specification needs a recorded amendment.
+
+### Repository Map
+
+# Repository map
+
+| Path | Role |
+| --- | --- |
+| `docs/specification.md` | Normative specification (source of truth). |
+| `docs/whitepaper.md` | Motivation and design narrative. |
+| `docs/decisions/` | Architecture decision records. |
+| `go/` | The implementation (static binary; module `github.com/buchk/TypeFerence/go`). |
+| `go/cmd/typeference-lsp/` | Language server for `.tfer`/`.yaml` authoring. |
+| `editors/vscode/` | VS Code client for the language server. |
+| `go/conformance/` | Determinism runner (`-update` regenerates digests). |
+| `conformance/` | Golden-file fixture corpus. |
+| `examples/helio` | Example organization used by tests and the quick start. |
+| `dist/` | Committed reference output of `examples/helio` (byte-compared in CI). |
+| `agents/maintainer/` | This definition; compiled into the root `AGENTS.md` and `dist-maintainer/`. |
+| `.github/workflows/ci.yml` | Build, test, determinism, and self-host drift gates. |
+
+The root `AGENTS.md` is generated from this definition. To change it, edit the
+resources under `agents/maintainer/` and run `make selfhost`; never edit the
+generated file directly.
+
+### Specification-first Workflow
+
+# Specification-first workflow
+
+The normative specification is `docs/specification.md` at the repository root. It is
+the source of truth; the Go implementation under `go/` is its reference realization.
+
+A semantic change alters valid source, composition, or compiled bytes. It follows:
+
+1. Amend `docs/specification.md`.
+2. Record the decision and rejected alternatives in `docs/decisions/`.
+3. Add or update `conformance/fixtures/`.
+4. Update the implementation until determinism passes.
+
+If implementation and specification disagree, the specification wins. Ambiguity is
+a specification bug, not permission for a private implementation rule.
+
+### Trust Model
+
+# Trust model invariants
+
+- The signature map lives outside the source root, avoiding a digest/signature cycle.
+- `signatureIntent.required` fails closed; unsigned staging only emits payloads for
+ an external signer.
+- TypeFerence imports externally produced signatures; it never signs, verifies
+ cryptographic validity, resolves keys, or dereferences trust URIs.
+- Identities are ASCII; internationalized authorities use punycode.
+
+Trust changes require a specification amendment, ADR, and conformance fixtures.
## Available skills
- `typeference-maintainer.audit-drift`: Confirms the committed AGENTS.md and maintainer bundle are exact build artifacts of this definition.
-- `typeference-maintainer.verify-conformance`: Runs the shared conformance suite on both implementations and reports any digest disagreement.
+- `typeference-maintainer.verify-conformance`: Runs the determinism suite and reports whether the compiler reproduces the committed digests.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 84e32f3..63ab0e4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,12 +3,50 @@
All notable changes to TypeFerence are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow semantic
versioning. Tool versions (this file) are independent of the typed-resource
-`schemaVersion` (currently 3) and the trust configuration `schemaVersion`
+`schemaVersion` (currently 4) and the trust configuration `schemaVersion`
(currently 1), which only change when the source formats change incompatibly.
TypeFerence is an experimental reference implementation; pre-1.0 versions make no
compatibility promises between minor versions.
+## [Unreleased]
+
+### Changed
+
+- **Closed v4 source language** ([ADR-0020](docs/decisions/0020-close-the-source-language.md)).
+ Native context types replace embedded JSON Schema, context values and typed
+ slots are complete compile-time values, `contextFiles` is removed, mode
+ requirements remain conditional, sealed abstract requirements fail, and
+ normal product entrypoints reject legacy v3 input.
+- **Build/link separation and source identity**
+ ([ADR-0022](docs/decisions/0022-build-link-and-source-identity.md)). Build emits
+ deterministic unlinked targets and integrity indexes; explicit deployment
+ files bind tools, modes, commands, environment references, and endpoints.
+ Linked Codex MCP configuration and neutral A2A cards are structurally
+ serialized without changing source or unlinked-target identity.
+- **Go-only implementation** ([ADR-0014](docs/decisions/0014-go-only-implementation.md)).
+ The C# reference implementation was retired; the Go implementation is now the
+ sole implementation. The specification stays normative in principle. The
+ cross-implementation conformance suite becomes a single-implementation
+ golden-file determinism suite over the same fixtures — the determinism
+ guarantee is unchanged.
+
+### Added
+
+- **Locked source packages and enterprise restore**
+ ([ADR-0021](docs/decisions/0021-restore-locked-source-packages.md)):
+ `pack`, `restore`, and `update`, canonical `.tferpkg` archives and lockfiles,
+ complete offline dependency materialization, scoped filesystem/HTTP/JFrog/
+ Azure Artifacts routes, and dependency provenance in target artifacts.
+- **`.tfer` source format** and object-model constructs (ADRs 0012–0019):
+ invocation-mode skill variants, user-defined typed context (`contextType`
+ refinement, context held by id, `requiresContextTypes`), tools as extern
+ declarations (`requiresTools`), capability exposure/visibility, and sealing
+ (`sealed`/`required` bindings). All additive and opt-in.
+- **Language server** (`typeference-lsp`): authoring and composition
+ diagnostics, completion, go-to-definition, and document symbols for `.tfer`
+ and `.yaml` sources.
+
## [0.0.4] - 2026-07-15
### Added
diff --git a/Directory.Build.props b/Directory.Build.props
deleted file mode 100644
index 1a31f06..0000000
--- a/Directory.Build.props
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- net10.0
- enable
- enable
- latest
- true
- true
- 0.0.4
-
-
diff --git a/Makefile b/Makefile
index 8b385df..5ffdf1f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,41 +1,37 @@
-# TypeFerence build entry points. Requires: Go 1.24+, .NET 10 SDK (reference
-# implementation only). All artifacts are deterministic; run `make conformance`
-# to verify both implementations agree byte-for-byte.
+# TypeFerence build entry points. Requires: Go 1.24+ and nothing else. All
+# artifacts are deterministic; run `make conformance` to verify the compiler
+# reproduces the committed digests byte-for-byte (ADR-0014).
GO ?= go
-DOTNET ?= dotnet
VERSION ?= dev
GOFLAGS := -trimpath
LDFLAGS := -s -w -X main.version=$(VERSION)
BINDIR := bin
-.PHONY: all build build-go build-dotnet test test-go test-dotnet conformance \
+.PHONY: all build build-go build-lsp test test-go conformance \
selfhost selfhost-check fmt vet clean release-binaries playground
all: build test
-build: build-go build-dotnet
+build: build-go build-lsp
# Single static binary, no runtime dependencies.
build-go:
cd go && CGO_ENABLED=0 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o ../$(BINDIR)/typeference$(shell $(GO) env GOEXE) ./cmd/typeference
-build-dotnet:
- $(DOTNET) build TypeFerence.slnx
+# Language server for .tfer/.yaml authoring diagnostics (internal/lsp).
+build-lsp:
+ cd go && CGO_ENABLED=0 $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o ../$(BINDIR)/typeference-lsp$(shell $(GO) env GOEXE) ./cmd/typeference-lsp
-test: test-go test-dotnet
+test: test-go
test-go:
cd go && $(GO) test ./...
-test-dotnet:
- $(DOTNET) test TypeFerence.slnx
-
-# Cross-implementation conformance: both compilers run the shared fixture
-# corpus; digests must match the manifests and each other.
+# Determinism suite: the compiler compiles the shared fixture corpus and must
+# reproduce the committed digests (ADR-0014).
conformance:
cd go && $(GO) test ./conformance -run TestConformance -v
- $(DOTNET) test TypeFerence.slnx --filter FullyQualifiedName~ConformanceSuiteTests
# Recompile the self-hosted maintainer definition (agents/maintainer) into its
# committed artifacts: dist-maintainer and the repository-root AGENTS.md.
@@ -50,7 +46,6 @@ selfhost-check: build-go
fmt:
cd go && gofmt -l -w .
- $(DOTNET) format TypeFerence.slnx
vet:
cd go && $(GO) vet ./...
diff --git a/README.md b/README.md
index eb59ce0..a830e38 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
TypeFerence is an experimental reference implementation of a typed definition and compilation layer for AI agents. It replaces sprawling, duplicated instruction files with Go-like composition: reusable profiles, agent embedding, structurally satisfied interfaces, versioned capabilities, skill implementations, deterministic compilation, provenance, and artifact diffing.
-Read the [whitepaper](docs/whitepaper.md), the [rendered PDF](output/pdf/typeference-whitepaper.pdf), the [draft v3 specification](docs/specification.md), or the [ARD alignment notes](docs/ard-alignment.md).
+Read the [whitepaper](docs/whitepaper.md), the [rendered PDF](output/pdf/typeference-whitepaper.pdf), the [draft v4 specification](docs/specification.md), or the [ARD alignment notes](docs/ard-alignment.md).
```text
helio/profiles/enterprise-defaults ──embedded by──> helio/profiles/person-defaults ──embedded by──> helio/executive-assistant
@@ -22,21 +22,21 @@ Agent runtime system prompts are like machine code: they are what the model actu
## Where it fits
```text
-TypeFerence source
- -> native agent artifacts
- -> optional ARD publication and discovery
- -> MCP, A2A, OpenAPI, or host-native invocation
- -> Codex, Copilot, Cursor, Yoke, or another runtime
+declared source packages
+ -> restore exact locked dependency tree
+ -> build deterministic unlinked target artifacts
+ -> link explicit deployment bindings
+ -> publish or run through the selected host
```
[Agentic Resource Discovery](https://agenticresourcediscovery.org/) helps clients find and verify deployed capabilities. TypeFerence addresses the earlier authoring problem: producing compatible native artifacts from one governed definition. Discovery portability does not itself provide definition portability.
-The long-term objective is behavioral equivalence: preserving declared organizational intent across supported hosts closely enough to be measured and governed. V3 provides the common typed source, deterministic adapters, and provenance needed to test that objective; it does not claim that different models or runtimes already behave identically.
+The long-term objective is behavioral equivalence: preserving declared organizational intent across supported hosts closely enough to be measured and governed. V4 provides the closed typed source, deterministic adapters, and provenance needed to test that objective; it does not claim that different models or runtimes already behave identically.
-The v3 source shape is deliberately small:
+The v4 source shape is deliberately small:
```yaml
-schemaVersion: 3
+schemaVersion: 4
kind: agent
id: helio/payments-repo-agent@1.0.0
embeds:
@@ -80,12 +80,16 @@ cd go && go build -o ../bin/ ./cmd/typeference && cd ..
./bin/typeference validate examples/helio
./bin/typeference build examples/helio --target all --out out --emit-ard --publisher-domain helio.example
+./bin/typeference link out/codex --deployment examples/deployment/helio.yaml --out linked/codex
+./bin/typeference link out/neutral --deployment examples/deployment/helio-a2a.yaml --out linked/a2a
./bin/typeference diff examples/helio --against dist --emit-ard --publisher-domain helio.example
./bin/typeference inspect helio/payments-repo-agent@1.0.0 --source examples/helio
```
-`build` writes the neutral bundle plus Codex, Copilot, and Cursor artifacts under
-`out/`. `diff` recompiles and byte-compares against the committed reference output
+`build` writes deterministic, unlinked neutral, Codex, Copilot, and Cursor
+artifacts under `out/`. It intentionally emits no active MCP command or endpoint.
+`link` validates an explicit external deployment file and materializes runtime
+configuration. `diff` recompiles and byte-compares against the committed reference output
in `dist/` — "No differences." is the determinism guarantee made visible: your
freshly built compiler reproduces the repository's artifacts exactly.
@@ -112,15 +116,22 @@ typeference version
```
`` in every command below is a directory of your own typed agent
-definitions (`schemaVersion: 3` YAML, shaped like the example above) — clone
+definitions (`schemaVersion: 4` YAML, shaped like the example above) — clone
this repository to point it at the bundled `examples/helio/` corpus instead.
```text
typeference validate [--trust-config path]
+ [--packages-dir obj/typeference/packages]
+typeference pack [--out package.tferpkg]
+typeference restore --feeds [--locked]
+ [--packages-dir obj/typeference/packages]
+typeference update --feeds
typeference build [--target all|neutral|codex|copilot|cursor] [--out dist]
+ [--packages-dir obj/typeference/packages]
[--emit-ard --publisher-domain example.com] [--trust-config path]
[--trust-signatures signatures.json] [--allow-unsigned-trust]
typeference inspect [--source path]
+typeference link --deployment --out
typeference diff --against [--target all]
[--emit-ard --publisher-domain example.com] [--trust-config path]
[--trust-signatures signatures.json] [--json] [--allow-unsigned-trust]
@@ -131,39 +142,64 @@ typeference equivalence score [--live] [--model id]
```
`validate` checks composition and structural typing without writing anything.
-`build` compiles to native target artifacts. `diff` recompiles and byte-compares
+`pack` creates a canonical source package. `restore` is the only dependency
+operation that contacts feeds; it commits exact identities and digests to
+`typeference.lock` and materializes the complete tree under
+`obj/typeference/packages`. Once a lock exists, `restore` honors it; `update`
+is the explicit re-resolution after exact manifest edits. `build` is offline
+and frozen. `link` is the separate
+environment-specific transform. `diff` recompiles and byte-compares
against an already-built directory — see [Quick start](#quick-start) above for
what "No differences." means. `eval` and `equivalence` are covered in
[Behavioral evals](#behavioral-evals) below; both default to a dry run that
never makes a network call.
-## Two implementations, one specification
+## One implementation, one specification
-The C# implementation under `src/` is the reference implementation; the Go
-implementation under `go/` is an independent second implementation. Both compile
-identical source to **byte-identical artifacts**, enforced by a shared conformance
-suite ([conformance/](conformance/README.md)) that runs on both implementations in
-CI — expected digests are generated by one implementation and independently
-verified by the other. That suite is what makes the
-[specification](docs/specification.md) real rather than aspirational.
+The Go implementation under `go/` is the reference implementation. The
+[specification](docs/specification.md) stays normative in principle — the
+abstraction is the contract, and anyone may realize it differently — but this
+repository's Go compiler is the one living answer ([ADR-0014](docs/decisions/0014-go-only-implementation.md)).
+Determinism is preserved and made visible by the
+[conformance suite](conformance/README.md): the compiler must reproduce the
+committed digests byte-for-byte.
-### Reference implementation (C#)
+An earlier C# reference implementation was retired when the project committed to
+being a tool rather than a multi-implementation standard. Determinism — the
+property that makes `diff` and the committed-digest guarantee real — is a property
+of one good compiler and was unaffected; only the second implementation, and the
+cross-implementation half of the conformance suite, went away. TypeFerence embeds
+no LLM provider. Deployment state is consumed only by the explicit linker and
+never changes source or unlinked-target identity.
-Requires the .NET 10 SDK.
+## Packages and enterprise feeds
-```powershell
-dotnet build TypeFerence.slnx
-dotnet src/TypeFerence.Cli/bin/Debug/net10.0/typeference.dll validate examples/helio
-dotnet src/TypeFerence.Cli/bin/Debug/net10.0/typeference.dll serve dist/neutral
+A project manifest declares exact source-package dependencies:
+
+```yaml
+schemaVersion: 2
+name: marathon/payments-agents
+version: 1.0.0
+dependencies:
+ marathon/enterprise-foundations: 3.1.0
```
-The MCP server (`typeference serve`) is currently implemented in the reference
-implementation only. It exposes tools such as:
+Feed routing is external to source identity. A filesystem/JFrog-compatible
+example looks like:
-- `executive-assistant.prepare-brief`
-- `payments-repo-agent.repository-status`
+```yaml
+schemaVersion: 1
+routes:
+ marathon:
+ kind: jfrog
+ baseUrl: https://artifacts.example/artifactory/typeference
+ credentialEnvironment: JFROG_ACCESS_TOKEN
+```
-Calls return a deterministic invocation package for the host agent. TypeFerence does not embed an LLM provider.
+`azureArtifacts` routes use Azure Universal Packages through the Azure CLI and
+its existing authentication. Package names replace `/` with `--` in Azure
+Artifacts. Lockfiles contain package identities, exports, dependency edges, and
+content digests—never feed URLs, credentials, or machine paths.
## Self-hosting
@@ -189,7 +225,11 @@ host, and `score` reports adherence per surface and agreement across surfaces,
listing every divergence. A scorecard is one observation per surface, not a proof;
see [ADR-0009](docs/decisions/0009-behavioral-equivalence-harness.md).
-`--emit-ard` publishes one canonical TypeFerence source-package entry and one precompiled bundle entry for each concrete agent and selected target. Compiled entries carry `derivedFrom` provenance back to the canonical source digest. ARD is an envelope: a target-aware installer must understand static Codex, Copilot, or Cursor bundles, while callable MCP or A2A resources require their own deployed endpoint and native card.
+`--emit-ard` emits one canonical TypeFerence source-package entry and one
+unlinked bundle entry for each concrete agent and selected target. Entries carry
+`derivedFrom` provenance back to the canonical explicit source-resource digest.
+Callable MCP or A2A publication requires linked provider/endpoint metadata;
+build never invents an address.
### Trust manifests
@@ -220,17 +260,15 @@ TypeFerence does not hold signing keys. An external signer can produce detached
## Repository map
-- `src/` - C# reference implementation: compiler, target adapters, CLI, and MCP runtime.
-- `go/` - independent Go implementation: compiler, target adapters, CLI, and eval harness.
-- `conformance/` - shared cross-implementation conformance fixtures (byte-identity contract).
+- `go/` - the Go implementation: compiler, target adapters, CLI, language server (`cmd/typeference-lsp`), and eval harness.
+- `conformance/` - canonical conformance fixtures (byte-identity contract).
- `examples/helio/` - fictional cross-domain organization.
- `web/playground/` - browser playground: the Go compiler built for `js/wasm`, plus the BETH operator console.
- `agents/maintainer/` - this repository's maintainer agent, defined in TypeFerence.
- `evals/` - behavioral eval scenarios and honest framing.
-- `docs/specification.md` - normative v3 behavior.
+- `docs/specification.md` - normative v4 behavior.
- `docs/decisions/` - architecture decision records.
- `docs/whitepaper.md` and `output/pdf/typeference-whitepaper.pdf` - design paper.
-- `tests/` - C# test suite: type system, targets, determinism, conformance runner, MCP integration.
- `CHANGELOG.md` and `docs/release-checklist.md` - versioning and release process.
## Design boundaries
@@ -238,11 +276,16 @@ TypeFerence does not hold signing keys. An external signer can produce detached
- Agents may embed multiple profiles or agents; profiles may embed other profiles; local slots and capability bindings resolve promoted-name ambiguity.
- Interfaces may embed interfaces and are satisfied structurally, without declarations on agents.
- Capabilities are explicit, versioned method slots; skills are concrete implementations that bind those capabilities.
-- Context is referenced and loaded only with the skill that needs it.
-- Target adapters emit platform-native shapes while retaining the portable fields each target supports.
+- Context is a first-class typed resource. Raw prose uses an explicit text-body
+ context type; there is no filesystem-path escape hatch.
+- Tools are declared runtime imports. Base and variant requirements remain
+ distinct until link selects deployment modes and providers.
+- Target adapters emit deterministic unlinked platform-native shapes; link
+ materializes active runtime configuration.
- Build output is deterministic and carries provenance.
-- No deployment state, hosted runtime, or model credentials in v3.
-- No ARD registry lifecycle, federation, dependency, install-safety, or deployment metadata in core TypeFerence semantics.
+- Source package dependencies restore to an exact committed graph before build.
+- No deployment state, hosted runtime, or model credentials participate in
+ source or build semantics.
- Structural validation does not guarantee identical LLM behavior across models or hosts.
- ARD publication wraps selected target outputs; it is not itself a compilation target or execution runtime.
diff --git a/TypeFerence.slnx b/TypeFerence.slnx
deleted file mode 100644
index 078e4a6..0000000
--- a/TypeFerence.slnx
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/agents/maintainer/agents/typeference-maintainer.agent.yaml b/agents/maintainer/agents/typeference-maintainer.agent.tfer
similarity index 81%
rename from agents/maintainer/agents/typeference-maintainer.agent.yaml
rename to agents/maintainer/agents/typeference-maintainer.agent.tfer
index bbeee3d..1a8e968 100644
--- a/agents/maintainer/agents/typeference-maintainer.agent.yaml
+++ b/agents/maintainer/agents/typeference-maintainer.agent.tfer
@@ -1,4 +1,5 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: agent
id: typeference/typeference-maintainer@0.1.0
displayName: TypeFerence Maintainer
@@ -9,6 +10,5 @@ embeds:
- typeference/profiles/trust-signing@0.1.0
- typeference/profiles/contribution-workflow@0.1.0
slots:
- repositoryMap: context/repository-map.md
-contextFiles:
- - context/repository-map.md
+ repositoryMap: typeference/context/repository-map@0.1.0
+---
diff --git a/agents/maintainer/capabilities/audit-drift.capability.yaml b/agents/maintainer/capabilities/audit-drift.capability.tfer
similarity index 94%
rename from agents/maintainer/capabilities/audit-drift.capability.yaml
rename to agents/maintainer/capabilities/audit-drift.capability.tfer
index 0f60f00..c5054a9 100644
--- a/agents/maintainer/capabilities/audit-drift.capability.yaml
+++ b/agents/maintainer/capabilities/audit-drift.capability.tfer
@@ -1,7 +1,9 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: capability
id: typeference/capabilities/audit-drift@0.1.0
displayName: Audit Self-Host Drift
description: Recompile the maintainer definition and report drift against the committed artifacts.
inputSchema: '{"type":"object","additionalProperties":false}'
outputSchema: '{"type":"object","properties":{"clean":{"type":"boolean"},"driftedFiles":{"type":"array","items":{"type":"string"}}},"required":["clean","driftedFiles"]}'
+---
diff --git a/agents/maintainer/capabilities/verify-conformance.capability.yaml b/agents/maintainer/capabilities/verify-conformance.capability.tfer
similarity index 71%
rename from agents/maintainer/capabilities/verify-conformance.capability.yaml
rename to agents/maintainer/capabilities/verify-conformance.capability.tfer
index b1489e2..4eae9e3 100644
--- a/agents/maintainer/capabilities/verify-conformance.capability.yaml
+++ b/agents/maintainer/capabilities/verify-conformance.capability.tfer
@@ -1,7 +1,9 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: capability
id: typeference/capabilities/verify-conformance@0.1.0
displayName: Verify Conformance
-description: Run both implementations against the shared conformance suite and report agreement.
+description: Run the determinism suite and report whether the compiler reproduces the committed digests.
inputSchema: '{"type":"object","additionalProperties":false}'
outputSchema: '{"type":"object","properties":{"passed":{"type":"boolean"},"mismatches":{"type":"array","items":{"type":"string"}}},"required":["passed","mismatches"]}'
+---
diff --git a/agents/maintainer/context-types/contribution-workflow.contexttype.tfer b/agents/maintainer/context-types/contribution-workflow.contexttype.tfer
new file mode 100644
index 0000000..cd2285e
--- /dev/null
+++ b/agents/maintainer/context-types/contribution-workflow.contexttype.tfer
@@ -0,0 +1,9 @@
+---
+schemaVersion: 4
+kind: contextType
+id: typeference/context-types/contribution-workflow@0.1.0
+displayName: Contribution Workflow
+body:
+ type: text
+ required: true
+---
diff --git a/agents/maintainer/context-types/determinism.contexttype.tfer b/agents/maintainer/context-types/determinism.contexttype.tfer
new file mode 100644
index 0000000..e611a00
--- /dev/null
+++ b/agents/maintainer/context-types/determinism.contexttype.tfer
@@ -0,0 +1,9 @@
+---
+schemaVersion: 4
+kind: contextType
+id: typeference/context-types/determinism@0.1.0
+displayName: Determinism Guarantees
+body:
+ type: text
+ required: true
+---
diff --git a/agents/maintainer/context-types/repository-map.contexttype.tfer b/agents/maintainer/context-types/repository-map.contexttype.tfer
new file mode 100644
index 0000000..9b06fa0
--- /dev/null
+++ b/agents/maintainer/context-types/repository-map.contexttype.tfer
@@ -0,0 +1,10 @@
+---
+schemaVersion: 4
+kind: contextType
+id: typeference/context-types/repository-map@0.1.0
+displayName: Repository Map
+description: A stable map of repository paths and their maintenance roles.
+body:
+ type: text
+ required: true
+---
diff --git a/agents/maintainer/context-types/spec-workflow.contexttype.tfer b/agents/maintainer/context-types/spec-workflow.contexttype.tfer
new file mode 100644
index 0000000..fd5b95c
--- /dev/null
+++ b/agents/maintainer/context-types/spec-workflow.contexttype.tfer
@@ -0,0 +1,9 @@
+---
+schemaVersion: 4
+kind: contextType
+id: typeference/context-types/spec-workflow@0.1.0
+displayName: Specification Workflow
+body:
+ type: text
+ required: true
+---
diff --git a/agents/maintainer/context-types/trust-model.contexttype.tfer b/agents/maintainer/context-types/trust-model.contexttype.tfer
new file mode 100644
index 0000000..ab90e5a
--- /dev/null
+++ b/agents/maintainer/context-types/trust-model.contexttype.tfer
@@ -0,0 +1,9 @@
+---
+schemaVersion: 4
+kind: contextType
+id: typeference/context-types/trust-model@0.1.0
+displayName: Trust Model
+body:
+ type: text
+ required: true
+---
diff --git a/agents/maintainer/context/contribution.md b/agents/maintainer/context/contribution.context.tfer
similarity index 69%
rename from agents/maintainer/context/contribution.md
rename to agents/maintainer/context/contribution.context.tfer
index 3d03d8b..2b87ab5 100644
--- a/agents/maintainer/context/contribution.md
+++ b/agents/maintainer/context/contribution.context.tfer
@@ -1,10 +1,16 @@
+---
+schemaVersion: 4
+kind: context
+id: typeference/context/contribution-workflow@0.1.0
+contextType: typeference/context-types/contribution-workflow@0.1.0
+displayName: Contribution Workflow
+---
# Contribution workflow
- Work on feature branches; never rewrite published history; never tag or publish a
release outside the checklist in `docs/release-checklist.md`.
-- Before any commit: `dotnet test TypeFerence.slnx`, `go test ./...` (from `go/`),
- and the conformance suite (`make conformance`) all pass. A commit that breaks any
- of them does not land.
+- Before any commit: `go test ./...` (from `go/`) and the determinism suite
+ (`make conformance`) both pass. A commit that breaks either does not land.
- Design decisions with real tradeoffs — spec semantics, canonical bytes, trust
model, dependencies — are recorded in `docs/decisions/` as numbered ADRs in the
same change.
diff --git a/agents/maintainer/context/determinism.md b/agents/maintainer/context/determinism.context.tfer
similarity index 59%
rename from agents/maintainer/context/determinism.md
rename to agents/maintainer/context/determinism.context.tfer
index 87c35d1..e42ff9f 100644
--- a/agents/maintainer/context/determinism.md
+++ b/agents/maintainer/context/determinism.context.tfer
@@ -1,21 +1,29 @@
+---
+schemaVersion: 4
+kind: context
+id: typeference/context/determinism@0.1.0
+contextType: typeference/context-types/determinism@0.1.0
+displayName: Determinism Guarantees
+---
# Determinism guarantees
-Identical source must compile to identical bytes — across repeated builds, across
-platforms, and across implementations. The guarantee is enforced, not aspirational:
+Identical source must compile to identical bytes — across repeated builds and
+across platforms. The guarantee is enforced, not aspirational:
- `conformance/fixtures/` records the expected `typeference-directory-v1` digest of
- every emitted target for 25+ fixtures. Both implementations run the corpus in CI.
+ every emitted target for the fixture corpus; the compiler must reproduce them in
+ CI (a golden-file determinism suite).
- The committed `dist/` tree is the fully materialized reference output for
- `examples/helio`; both implementations byte-compare against it in their own tests.
+ `examples/helio`; the compiler byte-compares against it in its tests.
- The committed root `AGENTS.md` and `dist-maintainer/` are build artifacts of
`agents/maintainer/`; CI recompiles the definition and fails on any drift.
Rules that protect the guarantee:
-- Digest values are regenerated (`go test ./conformance -update`, then verified by
- the C# runner), never typed by hand.
-- Canonical serialization is defined in `docs/specification.md` ("Deterministic
- compilation"); any change to it is a specification change with an ADR.
+- Digest values are regenerated (`go test ./conformance -update`), never typed by
+ hand.
+- Canonical serialization is defined in `docs/specification.md` ("Canonicalization");
+ any change to it is a specification change with an ADR.
- Nothing about determinism, provenance, or fail-closed behavior is ever relaxed to
make an unrelated change easier. If a change fights the determinism rules, the
change is wrong or the specification needs a recorded amendment.
diff --git a/agents/maintainer/context/repository-map.md b/agents/maintainer/context/repository-map.context.tfer
similarity index 54%
rename from agents/maintainer/context/repository-map.md
rename to agents/maintainer/context/repository-map.context.tfer
index 34b78dd..c3745a8 100644
--- a/agents/maintainer/context/repository-map.md
+++ b/agents/maintainer/context/repository-map.context.tfer
@@ -1,3 +1,10 @@
+---
+schemaVersion: 4
+kind: context
+id: typeference/context/repository-map@0.1.0
+contextType: typeference/context-types/repository-map@0.1.0
+displayName: Repository Map
+---
# Repository map
| Path | Role |
@@ -5,15 +12,15 @@
| `docs/specification.md` | Normative specification (source of truth). |
| `docs/whitepaper.md` | Motivation and design narrative. |
| `docs/decisions/` | Architecture decision records. |
-| `src/` | C# reference implementation (.NET 10). |
-| `go/` | Go implementation (static binary; module `github.com/buchk/TypeFerence/go`). |
-| `tests/` | C# test suite, including the conformance runner. |
-| `go/conformance/` | Go conformance runner (`-update` regenerates digests). |
-| `conformance/` | Shared cross-implementation fixture corpus. |
+| `go/` | The implementation (static binary; module `github.com/buchk/TypeFerence/go`). |
+| `go/cmd/typeference-lsp/` | Language server for `.tfer`/`.yaml` authoring. |
+| `editors/vscode/` | VS Code client for the language server. |
+| `go/conformance/` | Determinism runner (`-update` regenerates digests). |
+| `conformance/` | Golden-file fixture corpus. |
| `examples/helio` | Example organization used by tests and the quick start. |
| `dist/` | Committed reference output of `examples/helio` (byte-compared in CI). |
| `agents/maintainer/` | This definition; compiled into the root `AGENTS.md` and `dist-maintainer/`. |
-| `.github/workflows/ci.yml` | Build, test, conformance, and self-host drift gates. |
+| `.github/workflows/ci.yml` | Build, test, determinism, and self-host drift gates. |
The root `AGENTS.md` is generated from this definition. To change it, edit the
resources under `agents/maintainer/` and run `make selfhost`; never edit the
diff --git a/agents/maintainer/context/spec-workflow.context.tfer b/agents/maintainer/context/spec-workflow.context.tfer
new file mode 100644
index 0000000..38e0885
--- /dev/null
+++ b/agents/maintainer/context/spec-workflow.context.tfer
@@ -0,0 +1,21 @@
+---
+schemaVersion: 4
+kind: context
+id: typeference/context/spec-workflow@0.1.0
+contextType: typeference/context-types/spec-workflow@0.1.0
+displayName: Specification-first Workflow
+---
+# Specification-first workflow
+
+The normative specification is `docs/specification.md` at the repository root. It is
+the source of truth; the Go implementation under `go/` is its reference realization.
+
+A semantic change alters valid source, composition, or compiled bytes. It follows:
+
+1. Amend `docs/specification.md`.
+2. Record the decision and rejected alternatives in `docs/decisions/`.
+3. Add or update `conformance/fixtures/`.
+4. Update the implementation until determinism passes.
+
+If implementation and specification disagree, the specification wins. Ambiguity is
+a specification bug, not permission for a private implementation rule.
diff --git a/agents/maintainer/context/spec-workflow.md b/agents/maintainer/context/spec-workflow.md
deleted file mode 100644
index 2d6f48b..0000000
--- a/agents/maintainer/context/spec-workflow.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Specification-first workflow
-
-The normative specification is `docs/specification.md` at the repository root. It is
-the source of truth for both implementations:
-
-- C# reference implementation: `src/TypeFerence.Core`, `src/TypeFerence.Cli`
-- Go implementation: `go/`
-
-A change is *semantic* when it alters what source trees are valid, how composition
-resolves, or what bytes compilation emits. Semantic changes follow this order, in one
-reviewable change set:
-
-1. Amend `docs/specification.md`.
-2. Record the decision and rejected alternatives as an ADR in `docs/decisions/`.
-3. Add or update fixtures in `conformance/fixtures/` capturing the ruling.
-4. Update both implementations until the conformance suite passes on both.
-
-If an implementation is found to disagree with the specification, the specification
-wins. If the specification is ambiguous enough that two honest implementations could
-diverge, that is a specification bug: fix the text, do not encode a private
-interpretation in code.
diff --git a/agents/maintainer/context/trust-model.context.tfer b/agents/maintainer/context/trust-model.context.tfer
new file mode 100644
index 0000000..38db8ed
--- /dev/null
+++ b/agents/maintainer/context/trust-model.context.tfer
@@ -0,0 +1,17 @@
+---
+schemaVersion: 4
+kind: context
+id: typeference/context/trust-model@0.1.0
+contextType: typeference/context-types/trust-model@0.1.0
+displayName: Trust Model
+---
+# Trust model invariants
+
+- The signature map lives outside the source root, avoiding a digest/signature cycle.
+- `signatureIntent.required` fails closed; unsigned staging only emits payloads for
+ an external signer.
+- TypeFerence imports externally produced signatures; it never signs, verifies
+ cryptographic validity, resolves keys, or dereferences trust URIs.
+- Identities are ASCII; internationalized authorities use punycode.
+
+Trust changes require a specification amendment, ADR, and conformance fixtures.
diff --git a/agents/maintainer/context/trust-model.md b/agents/maintainer/context/trust-model.md
deleted file mode 100644
index 2569e47..0000000
--- a/agents/maintainer/context/trust-model.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Trust model invariants
-
-The trust model is declarative supply-chain metadata, specified in
-`docs/specification.md` ("Trust metadata compilation" and "Trust Manifest
-signatures"). The invariants that must survive every change:
-
-- **The signature map lives outside the source root.** The source-package digest
- covers every file under the source root; a signature stored inside would change
- the digest embedded in the very payload being signed. Both implementations reject
- a signature map path beneath the source root.
-- **Fail-closed stays fail-closed.** When `signatureIntent.required` is true and no
- signature is imported, publication fails. `--allow-unsigned-trust` exists solely
- to emit the payload for an external signer, and its output is visibly unsigned.
-- **TypeFerence never signs.** It imports externally produced compact detached JWS
- strings, validates their shape, and never verifies cryptographic validity,
- resolves keys, or dereferences any trust URI.
-- **Identities are ASCII** (punycode for internationalized authorities) so publisher
- alignment does not depend on implementation-specific IDN handling.
-
-Changes that touch any of these need a specification amendment, an ADR, and
-conformance fixtures before implementation.
diff --git a/agents/maintainer/profiles/contribution-workflow.profile.yaml b/agents/maintainer/profiles/contribution-workflow.profile.tfer
similarity index 75%
rename from agents/maintainer/profiles/contribution-workflow.profile.yaml
rename to agents/maintainer/profiles/contribution-workflow.profile.tfer
index 088c2d4..5b64571 100644
--- a/agents/maintainer/profiles/contribution-workflow.profile.yaml
+++ b/agents/maintainer/profiles/contribution-workflow.profile.tfer
@@ -1,12 +1,14 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: profile
id: typeference/profiles/contribution-workflow@0.1.0
displayName: Contribution Workflow
description: How changes to this repository are made, reviewed, and recorded.
workingNorms:
- - Every commit builds and passes both implementations' test suites and the shared conformance suite.
+ - Every commit builds and passes the test suite and the determinism suite.
- Decisions with real tradeoffs are recorded as ADRs in docs/decisions before the change merges.
- Documentation must be accurate against the code at the commit that includes it; no fabricated adoption, benchmarks, or endorsements.
- Commit messages are conventional and written for a critical human reader.
-contextFiles:
- - context/contribution.md
+context:
+ - typeference/context/contribution-workflow@0.1.0
+---
diff --git a/agents/maintainer/profiles/determinism-guardian.profile.yaml b/agents/maintainer/profiles/determinism-guardian.profile.tfer
similarity index 71%
rename from agents/maintainer/profiles/determinism-guardian.profile.yaml
rename to agents/maintainer/profiles/determinism-guardian.profile.tfer
index ae6b007..7c48440 100644
--- a/agents/maintainer/profiles/determinism-guardian.profile.yaml
+++ b/agents/maintainer/profiles/determinism-guardian.profile.tfer
@@ -1,15 +1,17 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: profile
id: typeference/profiles/determinism-guardian@0.1.0
displayName: Determinism Guardian
-description: Any codegen change must preserve byte-identical output across both implementations.
+description: Any codegen change must preserve byte-identical output, verified by the determinism suite.
workingNorms:
- - Any change to code generation must keep the C# and Go implementations byte-identical, verified by the conformance suite before merge.
+ - Any change to code generation must reproduce the committed digests, verified by the determinism suite before merge.
- Never weaken determinism, provenance, or fail-closed behavior to make a change easier.
- A conformance digest is regenerated only together with the specification change and ADR that justify it; never hand-edit a digest.
- Repeated builds from identical source must stay byte-identical on every platform.
-contextFiles:
- - context/determinism.md
+context:
+ - typeference/context/determinism@0.1.0
skills:
- ref: typeference/skills/verify-conformance@0.1.0
- ref: typeference/skills/audit-drift@0.1.0
+---
diff --git a/agents/maintainer/profiles/spec-conformance.profile.yaml b/agents/maintainer/profiles/spec-conformance.profile.tfer
similarity index 83%
rename from agents/maintainer/profiles/spec-conformance.profile.yaml
rename to agents/maintainer/profiles/spec-conformance.profile.tfer
index b3dfc3d..b281e9a 100644
--- a/agents/maintainer/profiles/spec-conformance.profile.yaml
+++ b/agents/maintainer/profiles/spec-conformance.profile.tfer
@@ -1,12 +1,14 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: profile
id: typeference/profiles/spec-conformance@0.1.0
displayName: Spec Conformance
-description: Semantic changes land in the specification first, then in both implementations, with conformance fixtures.
+description: Semantic changes land in the specification first, then in the implementation, with conformance fixtures.
workingNorms:
- Semantic changes land in docs/specification.md before either implementation changes behavior.
- Where the specification and an implementation disagree, the specification wins; record the ruling in docs/decisions and cover it with a conformance fixture.
- Every canonicalization or composition ruling ships with a fixture under conformance/fixtures in the same change.
- Never silently diverge from the specification; fixing the specification is allowed, silent divergence is not.
-contextFiles:
- - context/spec-workflow.md
+context:
+ - typeference/context/spec-workflow@0.1.0
+---
diff --git a/agents/maintainer/profiles/trust-signing.profile.yaml b/agents/maintainer/profiles/trust-signing.profile.tfer
similarity index 90%
rename from agents/maintainer/profiles/trust-signing.profile.yaml
rename to agents/maintainer/profiles/trust-signing.profile.tfer
index 0b0272d..2d817d3 100644
--- a/agents/maintainer/profiles/trust-signing.profile.yaml
+++ b/agents/maintainer/profiles/trust-signing.profile.tfer
@@ -1,4 +1,5 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: profile
id: typeference/profiles/trust-signing@0.1.0
displayName: Trust and Signing
@@ -8,5 +9,6 @@ workingNorms:
- signatureIntent.required fails closed; the unsigned-staging option exists solely to emit payloads for an external signer.
- TypeFerence imports externally produced signatures; it never signs, never verifies cryptographic validity, and never resolves keys.
- Trust metadata is declarative; never dereference identity, attestation, or provenance URIs during compilation.
-contextFiles:
- - context/trust-model.md
+context:
+ - typeference/context/trust-model@0.1.0
+---
diff --git a/agents/maintainer/skills/audit-drift.skill.yaml b/agents/maintainer/skills/audit-drift.skill.tfer
similarity index 91%
rename from agents/maintainer/skills/audit-drift.skill.yaml
rename to agents/maintainer/skills/audit-drift.skill.tfer
index e8bfaee..7174b14 100644
--- a/agents/maintainer/skills/audit-drift.skill.yaml
+++ b/agents/maintainer/skills/audit-drift.skill.tfer
@@ -1,4 +1,5 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: skill
id: typeference/skills/audit-drift@0.1.0
binds: typeference/capabilities/audit-drift@0.1.0
@@ -13,7 +14,8 @@ instructions: |
definition and its committed artifacts is a broken build: regenerate with
`make selfhost` and commit definition and artifacts together, or revert the
stray edit to the generated files.
-contextFiles:
- - context/determinism.md
+requiresContextTypes:
+ - typeference/context-types/determinism@0.1.0
inputSchema: '{"type":"object","additionalProperties":false}'
outputSchema: '{"type":"object","properties":{"clean":{"type":"boolean"},"driftedFiles":{"type":"array","items":{"type":"string"}}},"required":["clean","driftedFiles"]}'
+---
diff --git a/agents/maintainer/skills/verify-conformance.skill.tfer b/agents/maintainer/skills/verify-conformance.skill.tfer
new file mode 100644
index 0000000..13ae55a
--- /dev/null
+++ b/agents/maintainer/skills/verify-conformance.skill.tfer
@@ -0,0 +1,18 @@
+---
+schemaVersion: 4
+kind: skill
+id: typeference/skills/verify-conformance@0.1.0
+binds: typeference/capabilities/verify-conformance@0.1.0
+displayName: Verify Conformance Skill
+description: Runs the determinism suite and reports whether the compiler reproduces the committed digests.
+instructions: |
+ Run `go test ./conformance` from the go/ directory (or `make conformance`).
+ Report passed=true only when every fixture reproduces its committed digest.
+ List each failing fixture and target as a mismatch. Never resolve a mismatch
+ by editing a digest; find the compiler regression, or take the ruling to the
+ specification with an ADR and a regenerated fixture.
+requiresContextTypes:
+ - typeference/context-types/determinism@0.1.0
+inputSchema: '{"type":"object","additionalProperties":false}'
+outputSchema: '{"type":"object","properties":{"passed":{"type":"boolean"},"mismatches":{"type":"array","items":{"type":"string"}}},"required":["passed","mismatches"]}'
+---
diff --git a/agents/maintainer/skills/verify-conformance.skill.yaml b/agents/maintainer/skills/verify-conformance.skill.yaml
deleted file mode 100644
index 7cec76b..0000000
--- a/agents/maintainer/skills/verify-conformance.skill.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-schemaVersion: 3
-kind: skill
-id: typeference/skills/verify-conformance@0.1.0
-binds: typeference/capabilities/verify-conformance@0.1.0
-displayName: Verify Conformance Skill
-description: Runs the shared conformance suite on both implementations and reports any digest disagreement.
-instructions: |
- Run `go test ./conformance` from the go/ directory, then run
- `dotnet test TypeFerence.slnx --filter FullyQualifiedName~ConformanceSuiteTests`
- from the repository root (or `make conformance` for both). Report passed=true
- only when every fixture passes on both implementations. List each failing
- fixture and target as a mismatch. Never resolve a mismatch by editing a
- digest; find the diverging implementation or take the ruling to the
- specification with an ADR and a new fixture.
-contextFiles:
- - context/determinism.md
-inputSchema: '{"type":"object","additionalProperties":false}'
-outputSchema: '{"type":"object","properties":{"passed":{"type":"boolean"},"mismatches":{"type":"array","items":{"type":"string"}}},"required":["passed","mismatches"]}'
diff --git a/agents/maintainer/typeference.yaml b/agents/maintainer/typeference.yaml
new file mode 100644
index 0000000..8cad250
--- /dev/null
+++ b/agents/maintainer/typeference.yaml
@@ -0,0 +1,4 @@
+schemaVersion: 2
+name: typeference/maintainer
+version: 0.1.0
+publisher: typeference.example
diff --git a/conformance/README.md b/conformance/README.md
index 154df22..b017d7d 100644
--- a/conformance/README.md
+++ b/conformance/README.md
@@ -1,9 +1,11 @@
-# TypeFerence conformance suite
+# TypeFerence determinism suite
-Language-neutral fixtures that every conforming implementation must compile to
-byte-identical artifacts. This suite is what makes the specification real: the C#
-reference implementation and the Go implementation both run it in CI, and a digest
-mismatch on any fixture is a broken build.
+Language-neutral fixtures the compiler must reproduce as byte-identical
+artifacts. This suite pins the specification's canonical output: the Go
+implementation runs it in CI, and a digest mismatch on any fixture is a broken
+build. The fixtures and canonicalization rulings (ADR-0004) are the same corpus
+that once verified two implementations against each other; a single
+implementation now carries it forward as a golden-file guarantee (ADR-0014).
## Layout
@@ -29,22 +31,16 @@ All fixtures build with `--target all`.
## Running
-- Go implementation: `cd go && go test ./conformance`
-- Reference implementation: `dotnet test --filter FullyQualifiedName~ConformanceSuiteTests`
-- Both: `make conformance`
+`cd go && go test ./conformance` (or `make conformance`).
## Updating expectations
-Digests are generated by the Go runner and independently verified by the C# runner —
-a manifest can only be green when both implementations agree:
-
```
cd go && go test ./conformance -run TestConformance -update
-dotnet test TypeFerence.slnx --filter FullyQualifiedName~ConformanceSuiteTests
```
Never hand-edit a digest. If a digest changes, either the spec changed (requires an
-ADR) or an implementation broke.
+ADR) or the compiler broke.
## Byte fidelity
diff --git a/conformance/fixtures/001-minimal-agent/manifest.json b/conformance/fixtures/001-minimal-agent/manifest.json
index 6f59bcc..f8d35a0 100644
--- a/conformance/fixtures/001-minimal-agent/manifest.json
+++ b/conformance/fixtures/001-minimal-agent/manifest.json
@@ -2,9 +2,9 @@
"description": "Smallest valid source tree: one agent with defaults only.",
"expect": "success",
"digests": {
- "codex": "sha256:89ab7ee073b61c2be0a731b060d8d7b558693f9a7c3f76e3f489c349f9e6c3a0",
- "copilot": "sha256:46a9836ed41f7e861324a2749035548392063717937c8c05a7bc433c5542048d",
- "cursor": "sha256:9a6acc2525e6aab1fb36db60d8d97dba6d7681c4e53780ef6b39e9a921a1989b",
- "neutral": "sha256:bc64e751b5267801d8d0bcb2411e9ff574d0940c813c09fc187092d364a94f3b"
+ "codex": "sha256:438268f3c993be0f5e3c6468ef9324e5c304f644d004eb87730a0cf37147cc2b",
+ "copilot": "sha256:776a3a1eec5817b0099ae8ebd260d70d1a55fa03a73c3c9224ed18ad16c05b62",
+ "cursor": "sha256:146338e6f45c6e2efef8da6ad3c31d8a4f3d22f6e56c39679736df11eb25727d",
+ "neutral": "sha256:aa87280969c29d104b3b5b335cd6dca55c9ea033f52bae9b952d2904af7652f8"
}
}
diff --git a/conformance/fixtures/002-deep-embedding-chain/manifest.json b/conformance/fixtures/002-deep-embedding-chain/manifest.json
index beeea99..694fb0a 100644
--- a/conformance/fixtures/002-deep-embedding-chain/manifest.json
+++ b/conformance/fixtures/002-deep-embedding-chain/manifest.json
@@ -2,9 +2,9 @@
"description": "Five-level profile embedding chain; slots, norms, and contexts promote through every level in first-seen order.",
"expect": "success",
"digests": {
- "codex": "sha256:255141125ad53b9a9b88aa0c0072ec4fd5d8e42bb2b52787e78c5cf8f7a2076e",
- "copilot": "sha256:ff5d3ee5fe9cc0cb1538746f54a18663ca9c728b019c29d3b4ec31f2c6a70239",
- "cursor": "sha256:87f8d01f7c4f6a4526b260278204167441ff0108e16a34aa5e1b44b3a5811cf6",
- "neutral": "sha256:ec6c984a2c66a62c51d48e3087abe6acd8893665983392c660dc844d60f4aa01"
+ "codex": "sha256:0d1b7b3d1eec723f0add102cedaeb067a0b3728f8926b928a8c4e51160b7f252",
+ "copilot": "sha256:e3379b12835be953081072378c592004a2bc685dc4232513690c5e7c73ced1ba",
+ "cursor": "sha256:01b3e1a5beabda279cf806c2d4613cd8e459521848b448b375c519ad65ff2333",
+ "neutral": "sha256:3f41aec5211b24e6730f9fa8b5be22fad2f9036322d8b9ff3349015031437327"
}
}
diff --git a/conformance/fixtures/003-shallowest-wins/manifest.json b/conformance/fixtures/003-shallowest-wins/manifest.json
index 2344ca4..2bff77c 100644
--- a/conformance/fixtures/003-shallowest-wins/manifest.json
+++ b/conformance/fixtures/003-shallowest-wins/manifest.json
@@ -2,9 +2,9 @@
"description": "A slot declared deep in a chain is overridden by a shallower declaration on a sibling embed; the shallowest wins without ambiguity.",
"expect": "success",
"digests": {
- "codex": "sha256:2a4cad50dc1eb0921eb980454732032ff122905282fd99b1c0c543b6f7ab4584",
- "copilot": "sha256:cfd8e05958b7dc177d543b41620be1a6f9cc97c7e4f9a38049e6dcaba5386b68",
- "cursor": "sha256:545ed32a35b430d2fde17114d9a4ba6abfb5d440c00b2f66553591286ac6fd86",
- "neutral": "sha256:24357956d6be75dbba9dcd3ec26e9b4cbee36f21adef912d39a80652984d9257"
+ "codex": "sha256:55a4a3971d19128f3550999e78ff9c6eefa0bd294ba6980b1b5b41c724d0dfb7",
+ "copilot": "sha256:e20d0fb8cd69c7261e83b7279b45689087b201939b1472d201e77b19fbf2f734",
+ "cursor": "sha256:51c4dab8d6a9772f482135e102992a0c19d5a0f622bb20d498ca07ac67829b7f",
+ "neutral": "sha256:a1eaa014ba48f10a22075dac813f182a821339e62c1e1b88b0df5f344083ad9e"
}
}
diff --git a/conformance/fixtures/004-ambiguity-resolved-locally/manifest.json b/conformance/fixtures/004-ambiguity-resolved-locally/manifest.json
index e3976cf..d07b1c6 100644
--- a/conformance/fixtures/004-ambiguity-resolved-locally/manifest.json
+++ b/conformance/fixtures/004-ambiguity-resolved-locally/manifest.json
@@ -2,9 +2,9 @@
"description": "Two embeds declare the same slot at equal depth; the embedding agent resolves the conflict by declaring the slot locally.",
"expect": "success",
"digests": {
- "codex": "sha256:3bdb23f399432a6a3092ebe75c7e064cbc207b2e2e3ce24d58d468727f32788d",
- "copilot": "sha256:4a113bf774b0a10d28ab9cce2aeb7be6dddfccf57062fd9eca8f1e369a92f8bd",
- "cursor": "sha256:7c45ba6ddf77d52569597eb769e9961ded2eb4bd3483297cb04f85974a80d571",
- "neutral": "sha256:b8dfbda10fff422e0ff24fad135faabafcb728e0b00f273e1e4f60e6c9f34c5c"
+ "codex": "sha256:230bb32ed9e96b068daaafe6f9a0446d4f238fa8f419d5d3fd4027122112e81b",
+ "copilot": "sha256:8a2ab91bf77533341b0d5aa43af819564427addbb99f155244ef42e7022813ac",
+ "cursor": "sha256:9140f85c1177741dacf5d420aec3e02a1bfa3f47f89c7d4122ca251b30ec4543",
+ "neutral": "sha256:ec0284f68027e528b78205070169a5fac52e698969f8e79c4769ece90ce7a3ae"
}
}
diff --git a/conformance/fixtures/006-capability-promotion/manifest.json b/conformance/fixtures/006-capability-promotion/manifest.json
index 98202ab..c54f46d 100644
--- a/conformance/fixtures/006-capability-promotion/manifest.json
+++ b/conformance/fixtures/006-capability-promotion/manifest.json
@@ -2,9 +2,9 @@
"description": "A skill bound in an embedded profile is promoted into the agent; the dispatch name derives from the outer agent, not the profile.",
"expect": "success",
"digests": {
- "codex": "sha256:1609387a77916ac4cc0d0ba40959e3d0ab52214fa7577706037b578c8730e176",
- "copilot": "sha256:de256b844ada436fdfd7f8bc66a82a4931614a66c6280080057287aaf2000ec9",
- "cursor": "sha256:d1ac17dd7361af178e6f9bad31aa401207ea883058e400a8765c0deacbcaf32c",
- "neutral": "sha256:7b610852e3beb112d8e0c84d4683362de8ab1893a174c58b074fd5d39bf5137f"
+ "codex": "sha256:b76b1deb1f3bf4aa80e8066c3353f349a2d2906f7b4dee5888b3ec40fc7fd1e0",
+ "copilot": "sha256:6177a3a68c0363203f24d3d575061b1fa50136cbde097273272c83b47af840b4",
+ "cursor": "sha256:271da4376578d8ec61b6f619d22dd156393389ed073b1b01e9ff9b3ceeb56f25",
+ "neutral": "sha256:1b2cec6b313d9605e4a68e1be0e9799a062641d90de4012d1466775d435b04af"
}
}
diff --git a/conformance/fixtures/007-capability-override/manifest.json b/conformance/fixtures/007-capability-override/manifest.json
index a7a3625..3840ad8 100644
--- a/conformance/fixtures/007-capability-override/manifest.json
+++ b/conformance/fixtures/007-capability-override/manifest.json
@@ -2,9 +2,9 @@
"description": "The agent overrides a promoted skill with its own binding for the same capability; the implementation must preserve the capability's canonical schemas.",
"expect": "success",
"digests": {
- "codex": "sha256:46ccd5a077279895ebbf3fc0abefda0eb7185cbda465a2b89c085b3e78b0cae6",
- "copilot": "sha256:eedeb711a5672f38cf3d44a38eeb0fd7916ed296aec148555007884accd0c9b8",
- "cursor": "sha256:594544ea57631c99f7bf5a5a72b1d20a1f55720c5ba221d6bcb82a5f4a3ad95f",
- "neutral": "sha256:6683382bbd35ec2eee7919cebdae379f403e27e860be18ec0e175202840f7536"
+ "codex": "sha256:2fb138956ba3d5484b8d14045dc464872fd17ace90e826792b04a0b03d3edff1",
+ "copilot": "sha256:94455e965b4336477d3f1f2ba1ac66ef2cc8424f9ee7c8af646a23955221081d",
+ "cursor": "sha256:a5235def10312efb7c7f8811d2a7e20c8fb94c40a5e55ad663e4c7dad0b1381e",
+ "neutral": "sha256:aa57396bc2e927baa7ff7874525e89903229ff8872bf36bf0c7cef4e997348eb"
}
}
diff --git a/conformance/fixtures/009-interface-embedding/manifest.json b/conformance/fixtures/009-interface-embedding/manifest.json
index cfa7998..94d4565 100644
--- a/conformance/fixtures/009-interface-embedding/manifest.json
+++ b/conformance/fixtures/009-interface-embedding/manifest.json
@@ -2,9 +2,9 @@
"description": "Interfaces embed interfaces; agents satisfy them structurally without any implements declaration.",
"expect": "success",
"digests": {
- "codex": "sha256:696950759cf1292674604ef9aba90bde3d6b00fa57c6da93c7c40fef860c8453",
- "copilot": "sha256:57134957f13a8e428f674b1f27c8d6a1ba9634209641b89382bbec53f126ab16",
- "cursor": "sha256:e741736e62b509556be1b0bbb90221eb38e23efb7d0074788a3dbe14295b0c2d",
- "neutral": "sha256:f649f40e03ab11569f51f1b8a781350e42f769027e306018380dc2d68a29fc70"
+ "codex": "sha256:fe014bdcccba7b47409f9573851110f6e5b2a4237d2623d73b785cf6c3f92220",
+ "copilot": "sha256:9b0bbf96c5e3e3580f60e0b16db32886d6fbcaef7be8f4b72e4f639a4d4a24f6",
+ "cursor": "sha256:b05bce4e7291d3e5707f61614ebfffe718a817ac7cc4e27fa3c3c805232bb0ab",
+ "neutral": "sha256:c547022a6f6d5eecb7bbb3a00256c04f764176b0852775b95885320e0f71c109"
}
}
diff --git a/conformance/fixtures/010-unicode-content/manifest.json b/conformance/fixtures/010-unicode-content/manifest.json
index 9570ab2..df17c01 100644
--- a/conformance/fixtures/010-unicode-content/manifest.json
+++ b/conformance/fixtures/010-unicode-content/manifest.json
@@ -3,10 +3,10 @@
"expect": "success",
"emitArd": "conformance.example",
"digests": {
- "ard": "sha256:f1190bbcd05789e94f7bf2c05a2c44796cc7ab51cc5169a3da47158a58327c73",
- "codex": "sha256:7dce90627866e66ba3d8dc3256748e7e4725d7142ab33761c7d41e14957b01a0",
- "copilot": "sha256:a53323547d183be82fe7e12e8b1053a147ca4035dad589443a443e03731b466c",
- "cursor": "sha256:fe0a19e38eab9b2b2a0eefbaa9b06648a1a79e72d5e9e2a238ce9f5a5ad15331",
- "neutral": "sha256:f2090385d737e4ad96dec563f30fcd04452a6b5a28b217a431c39d8568cb70f3"
+ "ard": "sha256:935362fc35307f54f99b4a194b24adc0e8933ab8db577ff8d140bee743342803",
+ "codex": "sha256:5ea783bb80a100ae8c129f959fa4a226e3496bcdcf4b7877ba91c954afcb870c",
+ "copilot": "sha256:f971c494b10bc72a09a92e1d726cd1ea11c219009a265da99ff2a75320fa6dc9",
+ "cursor": "sha256:3abe49463bbcba1ce40233afc2025517ccf09f6ec92cd6e3fbb6684cbe5ded52",
+ "neutral": "sha256:9a03e13a0ce86ac45d0e2a3fee3308825fb38d3aace472afb6ae20f05ff63bc1"
}
}
diff --git a/conformance/fixtures/011-crlf-context/manifest.json b/conformance/fixtures/011-crlf-context/manifest.json
index 5157e4c..3267824 100644
--- a/conformance/fixtures/011-crlf-context/manifest.json
+++ b/conformance/fixtures/011-crlf-context/manifest.json
@@ -3,10 +3,10 @@
"expect": "success",
"emitArd": "conformance.example",
"digests": {
- "ard": "sha256:1ff7675f094666c36920dd4fbd90106d2045205f10d915083f2e26cb3460868b",
- "codex": "sha256:6ad4ea78b95528372196bcfad51893ed5faaad4bcaf8704da58d7ee3c8456db9",
- "copilot": "sha256:12d9d2450fb8e3d0e9acb954f7439ffeebb6e6623856c32604f29246ab40edbb",
- "cursor": "sha256:0c6da6936e8862b1e7380394313ed96906c5175d1239593286964be831150672",
- "neutral": "sha256:a762a3e6820ebaf9cfe06ae1460b2040f98f4466d3d1e08e62c280c08c0a544d"
+ "ard": "sha256:b6809f46c87f9a9ba9dc0e9c08486ca8832f32bd787bba7e550b6a52f0c5f0b4",
+ "codex": "sha256:1a661aa75f7ed116fc031082df6c6b67176a3f47f57ef23ae8a45b31b30d4a3d",
+ "copilot": "sha256:8ec151f1f6488cae8fe3908ab83c71f710b936d6089960b45a5a6f797412d104",
+ "cursor": "sha256:84bb8547856608de50666828c5aebb100d26593f070e86b11b0fae9e3a9845ad",
+ "neutral": "sha256:02ff538583bfb53e00d697bd3f40c6df130ef90d91e3f8dacc50fdf0872205b0"
}
}
diff --git a/conformance/fixtures/012-no-trailing-newline/manifest.json b/conformance/fixtures/012-no-trailing-newline/manifest.json
index 10af5ea..2edbc44 100644
--- a/conformance/fixtures/012-no-trailing-newline/manifest.json
+++ b/conformance/fixtures/012-no-trailing-newline/manifest.json
@@ -3,10 +3,10 @@
"expect": "success",
"emitArd": "conformance.example",
"digests": {
- "ard": "sha256:75a5b16fbf63f11e7c797601daea18d16a13ce214e93912bfd032eaf97cc2b6a",
- "codex": "sha256:32d3c4dddacf5f1c2b5455fc529ef32db23e01026cd2ce68c1bb06367b4c1e73",
- "copilot": "sha256:c8e8ebb9cb28714baaf5e68cfa6142ee8875d71e610bea20412889b55207ba23",
- "cursor": "sha256:4300dbfc02a62f08b02a9cf47fb60d640c65a4a90ee21bc7f3a27b739ecad2f4",
- "neutral": "sha256:75f98e1bb7777c63d628b4ed5faaeb9656541144ff8029d0641685714be8fb5d"
+ "ard": "sha256:4b9496db5cb67889b9dd7b24d87c8484900e84d9b509917af4ec2e1de7efa80b",
+ "codex": "sha256:341ad047c7558df8c1cc0dadc2ec46faabe638e0d2d9289190391d456cfc3a51",
+ "copilot": "sha256:923404cfd7ae28ee1204b0d040b1f9272a4ccb0ed8968e39091a5c3ad357cde3",
+ "cursor": "sha256:7e46ab37de2d5eaf6f6f8c8e7bce09bb81055a968bd0fca6d4ad3d24a8578a6f",
+ "neutral": "sha256:cdc615e5d541c114b7f846fb4d5f9e0a0b93639a7e675e15f3f9da765042c5ba"
}
}
diff --git a/conformance/fixtures/013-bom-source/manifest.json b/conformance/fixtures/013-bom-source/manifest.json
index a9a9a13..70f4f06 100644
--- a/conformance/fixtures/013-bom-source/manifest.json
+++ b/conformance/fixtures/013-bom-source/manifest.json
@@ -3,10 +3,10 @@
"expect": "success",
"emitArd": "conformance.example",
"digests": {
- "ard": "sha256:cc1c65064eabe4a925a45ba5f44473c866a2b47fa2aae73496f0e4c867c76653",
- "codex": "sha256:d8c29653d73c64b082127824246c78a40d4b077ec9a4686610b42680e0d3eebe",
- "copilot": "sha256:3a81319dab87c3cce2814919ebd4c39d42aaf64b11c080b599463007ad54e762",
- "cursor": "sha256:46b0f3ec863d160ccc3a35b5ff136818f046b8283ebaa0086cad83218e31cb88",
- "neutral": "sha256:b0093aa45b100a1e27821fc8664c00dc3603149eeba506bfc2336398ec33161e"
+ "ard": "sha256:0c40dc014563c1ad6566a4157bd2bfbbf40716cf370ed77bce3935abe5dee224",
+ "codex": "sha256:cc389750b88f730183d7920cc09449efb9e083f4f984743dd1810dd0959d1c1a",
+ "copilot": "sha256:b52e5a2cc7dbf42349b0732d190ebdc614a3ec8ecfb79397b02a6e3cfe812ee1",
+ "cursor": "sha256:6768779fe8abd4dc6acb49ebf2ab5890ad4ce2e89be7365c4c83be0a4d5a9b72",
+ "neutral": "sha256:a72417f05c9d9b7c9acffe7e3803450093a23266d4d91f48b2c7768e5e7722a1"
}
}
diff --git a/conformance/fixtures/014-empty-fields/manifest.json b/conformance/fixtures/014-empty-fields/manifest.json
index 68a269e..bb39311 100644
--- a/conformance/fixtures/014-empty-fields/manifest.json
+++ b/conformance/fixtures/014-empty-fields/manifest.json
@@ -2,9 +2,9 @@
"description": "Agent with no displayName and no description: displayName falls back to the resource id, empty strings serialize as empty.",
"expect": "success",
"digests": {
- "codex": "sha256:b8284cf759922da3be8b36f262b8439291d6854d0357fbf09978b372fc12ae97",
- "copilot": "sha256:b132b72e4925c49b55048cccb8966f3d05b88337ceba31dfdbc90c13b840a47b",
- "cursor": "sha256:98ca20ff2d462cdc163078f35cc7922875ae3e443a6a30054ba442c74e3d323c",
- "neutral": "sha256:1664054874a501adf6a11eb00cad28161e90f43fb38dd82256b7abc806fac541"
+ "codex": "sha256:25e235a770b1cf9500e0d19284aa08f0b0169cd99e37289a8bee86ec21d2dbec",
+ "copilot": "sha256:068c21cdef27c23660831936fb5f14e3f95b2f9c5aa5a2162813a05069ae6d65",
+ "cursor": "sha256:dce634e7d20af20a2a6fc91962a86679d970dc41d9a3717eae0278548564802e",
+ "neutral": "sha256:781c45346c34887e0a457d1bbd1c2d98fbc47d6ac127f0b35acd3528fc7aa7a6"
}
}
diff --git a/conformance/fixtures/015-kitchen-sink/manifest.json b/conformance/fixtures/015-kitchen-sink/manifest.json
index e0e7257..1f4f408 100644
--- a/conformance/fixtures/015-kitchen-sink/manifest.json
+++ b/conformance/fixtures/015-kitchen-sink/manifest.json
@@ -3,10 +3,10 @@
"expect": "success",
"emitArd": "conformance.example",
"digests": {
- "ard": "sha256:ec9641e8ba188bdc513a8058559204fe8f175047b2fb2e0da897f1193ec49f7b",
- "codex": "sha256:e7cbf898886493c2732b140f46dd43ac1100ae4d3a1dcee2f49085eeea5b76f2",
- "copilot": "sha256:c562cf680d46f02891fb4679887ab508de751ed03bcd70ff9b3b11d405587087",
- "cursor": "sha256:627fc2d6d7349e5c9c2d556f23cb41f89685fbb46268c64c453b8639e76f992b",
- "neutral": "sha256:3299639f297a9b6b6ac3bc27fb12febe47ac30aee53c85e278702c876177b222"
+ "ard": "sha256:e884cf2aa0f1f1592e352f69743fd500ac3d134d5557840f914b049d4a8df9b8",
+ "codex": "sha256:0bdf3931204d38a5eaac23bebac717d6af0ba3a0365ba1eae7265900c1d55a23",
+ "copilot": "sha256:90a9fd099cc92c388ff47b7b8c615a37f3a64ed420d41e7aea4ce893a5afee51",
+ "cursor": "sha256:e2645a024f46a7d0e25f1a0b950b0f10d0d48f40bc86a412e8c603faab78a9bf",
+ "neutral": "sha256:910fb6b92ee3280094e03ed3fc380f11c38fcbba4b8b2ecd8f1bd1aadfa58349"
}
}
diff --git a/conformance/fixtures/020-trust-unsigned/manifest.json b/conformance/fixtures/020-trust-unsigned/manifest.json
index 805ffec..3d4883c 100644
--- a/conformance/fixtures/020-trust-unsigned/manifest.json
+++ b/conformance/fixtures/020-trust-unsigned/manifest.json
@@ -3,10 +3,10 @@
"expect": "success",
"emitArd": "conformance.example",
"digests": {
- "ard": "sha256:d9333955fad318f98dbb7257dc5d47a011f5202039384ea797a66af59ea154d8",
- "codex": "sha256:f54b22db84e9cb5af0f7b2acf8678491732c1cbc51256f69b4628b5e0c028711",
- "copilot": "sha256:3adb2e6ab82aac6c9b47e2602a65a1bdd35db4e384daa4c9e5fbf813e5ce35f7",
- "cursor": "sha256:768c459cf6af8dfb5553ac684d52c2dd793ac4ba704ccdf42849ff7dac76f5a7",
- "neutral": "sha256:ae62d4707320decf06df0a695aef0968dfd5aef933613afed88ec40075a128d8"
+ "ard": "sha256:4f83054ebd06afe58cdf30180fe4c2c8aa9e3bed30643f9f446f9facddcb36a2",
+ "codex": "sha256:7afb5044bc8b660bcc78b5196b6245f913a77cf8f324ac359f7044a44f1c74a6",
+ "copilot": "sha256:9d2dcd85b8686c5fc61d08498bd8be2a57ef9196c1a2f7c1775ff5aef5369a1c",
+ "cursor": "sha256:8858b78ab19dae9465583bef2ce75d372503cf37be3277973c8707b5e5220946",
+ "neutral": "sha256:f26de1982d8169449501d2a081c8d93d866dec82ec6cff7b4a47ad122d846ab6"
}
}
diff --git a/conformance/fixtures/022-trust-signed/manifest.json b/conformance/fixtures/022-trust-signed/manifest.json
index 9e4461e..012047d 100644
--- a/conformance/fixtures/022-trust-signed/manifest.json
+++ b/conformance/fixtures/022-trust-signed/manifest.json
@@ -4,10 +4,10 @@
"emitArd": "conformance.example",
"trustSignatures": "signatures.json",
"digests": {
- "ard": "sha256:1bc4f382997259f31e278fad45dd418929221e332c6a91430aed974964726109",
- "codex": "sha256:3131b8acaefdfd4687d43a84f887d3c9c09550b3e1d783052ce99d44e959e37b",
- "copilot": "sha256:db822bb5584023ec0ed69aba3b9469380600cc0798f4a3a6b0c6bbfc77382a65",
- "cursor": "sha256:757ff81f6a161693e61b5d9e6a4b55441c2a621d6fa01114503f1d372915b744",
- "neutral": "sha256:dc6ac18ce290c31febcfabd385c0495f83d360f53fa02249a1f9c1c6e42f8439"
+ "ard": "sha256:13a25850e65ba37ee5079c82c0393e41f5910adf90c85e3f0bb148cc7bd5a5e9",
+ "codex": "sha256:725b0c33b5bae0011d18dbb69d3e786cbedee12c883bdbbce8719f1420bf6f01",
+ "copilot": "sha256:57a16f991779893ebcfc3aeebfab8cc5947ea16c259328e1ef9e2f306893e7b4",
+ "cursor": "sha256:416b78f44a98529c453ecbb25837d64070457b230939ab852cf193740821e21d",
+ "neutral": "sha256:5a19f82a1dea0712f019dbec213fc02b46f60bbd006075169c972a2f8c665694"
}
}
diff --git a/conformance/fixtures/024-number-token-preservation/manifest.json b/conformance/fixtures/024-number-token-preservation/manifest.json
index a80cff2..dea891e 100644
--- a/conformance/fixtures/024-number-token-preservation/manifest.json
+++ b/conformance/fixtures/024-number-token-preservation/manifest.json
@@ -2,9 +2,9 @@
"description": "JSON number tokens in schemas (1.0, 1e5, -0, 0.10) must survive canonicalization byte-for-byte in both capability and implementation.",
"expect": "success",
"digests": {
- "codex": "sha256:4e68ed1620e1cf4dcb498e1f2b5ad4d8cceb650d96b831f94e948eaa7068365e",
- "copilot": "sha256:f11ea44b8f7763ffb2534cacbe3360bf2e80ad61cc68ebd5571d9d11b4d19fd2",
- "cursor": "sha256:fceab32b26b09577e5e2a08f557a26701c5175c2fdbf5e4e1c8d2ec3b2826b6d",
- "neutral": "sha256:3bd8e2d7d8181cab633f36500d71d120043a9eebd37989bd93db27c9a9c27a51"
+ "codex": "sha256:30ad5cec2e55a5f9b425300876e48ee10a5384a0c3fefbefd9d55f36cf18cb7a",
+ "copilot": "sha256:660e8f3678372db8a3776efc09ed293ff25c7d8120e4605e567004803f982ac5",
+ "cursor": "sha256:d979f5b8c6d22a815ca7a5c92bd0a3e0dcf34e11e1b94dce44f636f38c51d84a",
+ "neutral": "sha256:a94bc184650b3823ed17e1847bc86178ab713b98d6d4d662f6af5d0ead153801"
}
}
diff --git a/conformance/fixtures/025-promoted-skill-context/manifest.json b/conformance/fixtures/025-promoted-skill-context/manifest.json
index c813cc4..5185064 100644
--- a/conformance/fixtures/025-promoted-skill-context/manifest.json
+++ b/conformance/fixtures/025-promoted-skill-context/manifest.json
@@ -2,9 +2,9 @@
"description": "A promoted skill keeps the context list resolved where it was bound; the outer agent's extra context files do not leak into the promoted skill.",
"expect": "success",
"digests": {
- "codex": "sha256:aadc135137def478e6661a47eaabb840edad266a2cc55b1c5f58f57ef7ed357a",
- "copilot": "sha256:9f9a77344aa6d67b0120e9b7b52ca2ea15935d59868a92228f7f2524e3d3986e",
- "cursor": "sha256:9abe4d49483031e3be6fee8d992221abc89b875d3d46a63f8a8c77dbaa40f686",
- "neutral": "sha256:61f9c3147c940916bdd142b5709c13ffccd8f0b9865e5156bea9577d50eda8a0"
+ "codex": "sha256:0652372175cefb1b3db71d85cc27e0087dd431ff6269a11474feb60c1108227b",
+ "copilot": "sha256:ea06dd4373a52a0ec61ff0ba492702b3cb643dc1dccc87e5b795a9d01bec984f",
+ "cursor": "sha256:41403014da103127dbef1ac5e7af4176e218413c7d70a2394e39b97e0e6138eb",
+ "neutral": "sha256:a9d91a89743b6efa7cd9eb122abf38f5eec1e87ca3f859400b5a2e6924dbfdf5"
}
}
diff --git a/conformance/fixtures/026-digest-path-order/manifest.json b/conformance/fixtures/026-digest-path-order/manifest.json
index fbb6974..030f384 100644
--- a/conformance/fixtures/026-digest-path-order/manifest.json
+++ b/conformance/fixtures/026-digest-path-order/manifest.json
@@ -3,10 +3,10 @@
"expect": "success",
"emitArd": "conformance.example",
"digests": {
- "ard": "sha256:7f65fef46be2e637ac8abeae54c4c863aac614b0d3e56229d87d49ebd6ffcbb3",
- "codex": "sha256:0c0f74933fd1dac0b17571ad4bc1eb83f61a22d7e896f9e1ccf4ba065bd21614",
- "copilot": "sha256:d343e3cd9f97702e5d71cbb2a511258856bad72102f0e671587e10a5c935f667",
- "cursor": "sha256:d9334f15e7c2547c33650a2fd6185550a3531e804f36dffa33ae04c601d8c96c",
- "neutral": "sha256:39c120ab3ea58d77e2b2d581e3b30edc5ee6807f762f964221b6819739f2df6f"
+ "ard": "sha256:33edbffdd6bddcdc861a6fd5531a49966e66427d7f083404a1b3b20fb7ebcc6d",
+ "codex": "sha256:4d27a56e0d4385f50b97a058e9abab6d99dd82fadbf5a7934bbd74079e98f2cf",
+ "copilot": "sha256:f01a3702af9154377bc78254368fd135cd0d628675c8ad805916451c3a37266e",
+ "cursor": "sha256:0522cfc52450ca99ce5c4548f518ca77546cb4eeb9ce0f74f9759349d9fec264",
+ "neutral": "sha256:80e54f8576f52bb88bf3b8d9034521e498e5342a29036efdbc1841910c4134e8"
}
}
diff --git a/conformance/fixtures/027-native-context-values/manifest.json b/conformance/fixtures/027-native-context-values/manifest.json
new file mode 100644
index 0000000..dff6372
--- /dev/null
+++ b/conformance/fixtures/027-native-context-values/manifest.json
@@ -0,0 +1,10 @@
+{
+ "description": "Version 4 native context fields, defaults, collections, and text body are validated and emitted into every target.",
+ "expect": "success",
+ "digests": {
+ "codex": "sha256:cc739728ef37b68227cf34204329522d078c5640af6ff25434f32121299aaf13",
+ "copilot": "sha256:479af014ed611479b20caa3ded1c0ff09800008b792b20614653cde3e3905b30",
+ "cursor": "sha256:c3a11b2bab341429301a82677e5f923edc27800a703dc7685054b584d1595f04",
+ "neutral": "sha256:581271f02a57e6dbecf7026c9bdf94a9c8a7b03386dfcee7c75993e8e79feef0"
+ }
+}
diff --git a/conformance/fixtures/027-native-context-values/source/agent.yaml b/conformance/fixtures/027-native-context-values/source/agent.yaml
new file mode 100644
index 0000000..984450e
--- /dev/null
+++ b/conformance/fixtures/027-native-context-values/source/agent.yaml
@@ -0,0 +1,5 @@
+schemaVersion: 4
+kind: agent
+id: conformance/agents/native-context@1.0.0
+context:
+ - conformance/context/team@1.0.0
diff --git a/conformance/fixtures/027-native-context-values/source/team-type.yaml b/conformance/fixtures/027-native-context-values/source/team-type.yaml
new file mode 100644
index 0000000..2daf3f2
--- /dev/null
+++ b/conformance/fixtures/027-native-context-values/source/team-type.yaml
@@ -0,0 +1,17 @@
+schemaVersion: 4
+kind: contextType
+id: conformance/context-types/team@1.0.0
+fields:
+ owner:
+ type: string
+ required: true
+ participants:
+ type:
+ list: string
+ required: true
+ governed:
+ type: boolean
+ default: false
+body:
+ type: text
+ required: true
diff --git a/conformance/fixtures/027-native-context-values/source/team.tfer b/conformance/fixtures/027-native-context-values/source/team.tfer
new file mode 100644
index 0000000..94f6e4d
--- /dev/null
+++ b/conformance/fixtures/027-native-context-values/source/team.tfer
@@ -0,0 +1,10 @@
+---
+schemaVersion: 4
+kind: context
+id: conformance/context/team@1.0.0
+contextType: conformance/context-types/team@1.0.0
+values:
+ owner: payments
+ participants: [Ari, Sam]
+---
+Stable team context.
diff --git a/conformance/fixtures/028-context-files-closed-error/manifest.json b/conformance/fixtures/028-context-files-closed-error/manifest.json
new file mode 100644
index 0000000..60b7e22
--- /dev/null
+++ b/conformance/fixtures/028-context-files-closed-error/manifest.json
@@ -0,0 +1,4 @@
+{
+ "description": "Version 4 rejects contextFiles instead of retaining a raw-path escape hatch.",
+ "expect": "error"
+}
diff --git a/conformance/fixtures/028-context-files-closed-error/source/agent.yaml b/conformance/fixtures/028-context-files-closed-error/source/agent.yaml
new file mode 100644
index 0000000..b6fb904
--- /dev/null
+++ b/conformance/fixtures/028-context-files-closed-error/source/agent.yaml
@@ -0,0 +1,5 @@
+schemaVersion: 4
+kind: agent
+id: conformance/agents/context-files-closed@1.0.0
+contextFiles:
+ - notes.md
diff --git a/conformance/fixtures/028-context-files-closed-error/source/notes.md b/conformance/fixtures/028-context-files-closed-error/source/notes.md
new file mode 100644
index 0000000..9a4c873
--- /dev/null
+++ b/conformance/fixtures/028-context-files-closed-error/source/notes.md
@@ -0,0 +1 @@
+This path must not be accepted by schema version 4.
diff --git a/conformance/fixtures/029-abstract-sealed-error/manifest.json b/conformance/fixtures/029-abstract-sealed-error/manifest.json
new file mode 100644
index 0000000..c81cf1c
--- /dev/null
+++ b/conformance/fixtures/029-abstract-sealed-error/manifest.json
@@ -0,0 +1,4 @@
+{
+ "description": "An abstract required capability cannot be sealed because there is no implementation to protect.",
+ "expect": "error"
+}
diff --git a/conformance/fixtures/029-abstract-sealed-error/source/capability.yaml b/conformance/fixtures/029-abstract-sealed-error/source/capability.yaml
new file mode 100644
index 0000000..a2552c1
--- /dev/null
+++ b/conformance/fixtures/029-abstract-sealed-error/source/capability.yaml
@@ -0,0 +1,3 @@
+schemaVersion: 4
+kind: capability
+id: conformance/capabilities/audit@1.0.0
diff --git a/conformance/fixtures/029-abstract-sealed-error/source/profile.yaml b/conformance/fixtures/029-abstract-sealed-error/source/profile.yaml
new file mode 100644
index 0000000..1f580a2
--- /dev/null
+++ b/conformance/fixtures/029-abstract-sealed-error/source/profile.yaml
@@ -0,0 +1,7 @@
+schemaVersion: 4
+kind: profile
+id: conformance/profiles/audit@1.0.0
+skills:
+ - capability: conformance/capabilities/audit@1.0.0
+ required: true
+ sealed: true
diff --git a/conformance/fixtures/030-disjoint-context-policy-error/manifest.json b/conformance/fixtures/030-disjoint-context-policy-error/manifest.json
new file mode 100644
index 0000000..ec4c912
--- /dev/null
+++ b/conformance/fixtures/030-disjoint-context-policy-error/manifest.json
@@ -0,0 +1,4 @@
+{
+ "description": "Disjoint context allow-lists compose to an explicit deny-all policy and fail closed.",
+ "expect": "error"
+}
diff --git a/conformance/fixtures/030-disjoint-context-policy-error/source/agent.yaml b/conformance/fixtures/030-disjoint-context-policy-error/source/agent.yaml
new file mode 100644
index 0000000..d234475
--- /dev/null
+++ b/conformance/fixtures/030-disjoint-context-policy-error/source/agent.yaml
@@ -0,0 +1,8 @@
+schemaVersion: 4
+kind: agent
+id: conformance/agents/disjoint@1.0.0
+embeds:
+ - conformance/profiles/left@1.0.0
+ - conformance/profiles/right@1.0.0
+context:
+ - conformance/context/left@1.0.0
diff --git a/conformance/fixtures/030-disjoint-context-policy-error/source/context.tfer b/conformance/fixtures/030-disjoint-context-policy-error/source/context.tfer
new file mode 100644
index 0000000..48488bd
--- /dev/null
+++ b/conformance/fixtures/030-disjoint-context-policy-error/source/context.tfer
@@ -0,0 +1,6 @@
+---
+schemaVersion: 4
+kind: context
+id: conformance/context/left@1.0.0
+contextType: conformance/context-types/left@1.0.0
+---
diff --git a/conformance/fixtures/030-disjoint-context-policy-error/source/left.yaml b/conformance/fixtures/030-disjoint-context-policy-error/source/left.yaml
new file mode 100644
index 0000000..cdae445
--- /dev/null
+++ b/conformance/fixtures/030-disjoint-context-policy-error/source/left.yaml
@@ -0,0 +1,5 @@
+schemaVersion: 4
+kind: profile
+id: conformance/profiles/left@1.0.0
+allowedContextTypes:
+ - conformance/context-types/left@1.0.0
diff --git a/conformance/fixtures/030-disjoint-context-policy-error/source/right-type.yaml b/conformance/fixtures/030-disjoint-context-policy-error/source/right-type.yaml
new file mode 100644
index 0000000..6556915
--- /dev/null
+++ b/conformance/fixtures/030-disjoint-context-policy-error/source/right-type.yaml
@@ -0,0 +1,3 @@
+schemaVersion: 4
+kind: contextType
+id: conformance/context-types/right@1.0.0
diff --git a/conformance/fixtures/030-disjoint-context-policy-error/source/right.yaml b/conformance/fixtures/030-disjoint-context-policy-error/source/right.yaml
new file mode 100644
index 0000000..06bc6d3
--- /dev/null
+++ b/conformance/fixtures/030-disjoint-context-policy-error/source/right.yaml
@@ -0,0 +1,5 @@
+schemaVersion: 4
+kind: profile
+id: conformance/profiles/right@1.0.0
+allowedContextTypes:
+ - conformance/context-types/right@1.0.0
diff --git a/conformance/fixtures/030-disjoint-context-policy-error/source/types.yaml b/conformance/fixtures/030-disjoint-context-policy-error/source/types.yaml
new file mode 100644
index 0000000..f8cdc2c
--- /dev/null
+++ b/conformance/fixtures/030-disjoint-context-policy-error/source/types.yaml
@@ -0,0 +1,3 @@
+schemaVersion: 4
+kind: contextType
+id: conformance/context-types/left@1.0.0
diff --git a/conformance/fixtures/031-mode-requirements/manifest.json b/conformance/fixtures/031-mode-requirements/manifest.json
new file mode 100644
index 0000000..04713e0
--- /dev/null
+++ b/conformance/fixtures/031-mode-requirements/manifest.json
@@ -0,0 +1,10 @@
+{
+ "description": "Base tool requirements apply to every mode while variant requirements stay attached only to their mode.",
+ "expect": "success",
+ "digests": {
+ "codex": "sha256:cf083d0bed01e997eabe259ff02bf9a27b8f44c18c60dc66d79df1e3bebb4fca",
+ "copilot": "sha256:4ee1987f0cb9bef278e135b2453ee5c37605f1fb8d5a7c5697cea49ab5758636",
+ "cursor": "sha256:91a9ab515687a0e45b690b38b0ee1352d8659a77b2af794830f5cf50a3d77599",
+ "neutral": "sha256:e122a0fb237b8fe3f4dd59e414e1c277ada6eb678a0c3ea81c12536f6a6339c6"
+ }
+}
diff --git a/conformance/fixtures/031-mode-requirements/source/agent.yaml b/conformance/fixtures/031-mode-requirements/source/agent.yaml
new file mode 100644
index 0000000..b8638f6
--- /dev/null
+++ b/conformance/fixtures/031-mode-requirements/source/agent.yaml
@@ -0,0 +1,5 @@
+schemaVersion: 4
+kind: agent
+id: conformance/agents/modes@1.0.0
+skills:
+ - ref: conformance/skills/status@1.0.0
diff --git a/conformance/fixtures/031-mode-requirements/source/build-signals.yaml b/conformance/fixtures/031-mode-requirements/source/build-signals.yaml
new file mode 100644
index 0000000..62e58d5
--- /dev/null
+++ b/conformance/fixtures/031-mode-requirements/source/build-signals.yaml
@@ -0,0 +1,3 @@
+schemaVersion: 4
+kind: tool
+id: conformance/tools/build-signals@1.0.0
diff --git a/conformance/fixtures/031-mode-requirements/source/capability.yaml b/conformance/fixtures/031-mode-requirements/source/capability.yaml
new file mode 100644
index 0000000..ecd77b9
--- /dev/null
+++ b/conformance/fixtures/031-mode-requirements/source/capability.yaml
@@ -0,0 +1,3 @@
+schemaVersion: 4
+kind: capability
+id: conformance/capabilities/status@1.0.0
diff --git a/conformance/fixtures/031-mode-requirements/source/skill.yaml b/conformance/fixtures/031-mode-requirements/source/skill.yaml
new file mode 100644
index 0000000..59ad332
--- /dev/null
+++ b/conformance/fixtures/031-mode-requirements/source/skill.yaml
@@ -0,0 +1,13 @@
+schemaVersion: 4
+kind: skill
+id: conformance/skills/status@1.0.0
+binds: conformance/capabilities/status@1.0.0
+requiresTools:
+ - conformance/tools/repository@1.0.0
+variants:
+ manual:
+ instructions: Explain the evidence.
+ pipeline:
+ instructions: Emit strict JSON.
+ requiresTools:
+ - conformance/tools/build-signals@1.0.0
diff --git a/conformance/fixtures/031-mode-requirements/source/tools.yaml b/conformance/fixtures/031-mode-requirements/source/tools.yaml
new file mode 100644
index 0000000..c15710e
--- /dev/null
+++ b/conformance/fixtures/031-mode-requirements/source/tools.yaml
@@ -0,0 +1,3 @@
+schemaVersion: 4
+kind: tool
+id: conformance/tools/repository@1.0.0
diff --git a/conformance/fixtures/032-typed-slot-contract/manifest.json b/conformance/fixtures/032-typed-slot-contract/manifest.json
new file mode 100644
index 0000000..8a41216
--- /dev/null
+++ b/conformance/fixtures/032-typed-slot-contract/manifest.json
@@ -0,0 +1,10 @@
+{
+ "description": "A version 4 interface slot contract is satisfied only by context of the required type or a refinement.",
+ "expect": "success",
+ "digests": {
+ "codex": "sha256:ccfe958da41c39059ee843cf0d2b4c149846e7c606af4f6fac35bd6178cecad7",
+ "copilot": "sha256:198f125c51bcf0ff75afb9be97e26f7d397cd809e3ed78a4aabc1ca5e26cfd7f",
+ "cursor": "sha256:da59cf975f8d7b843ef7cfca3dbf22ef08aeac75120067980e6d66d4765f1bb4",
+ "neutral": "sha256:0d4f50d78ac105776f840ff7ec97c3c11c327bac38faf8ffd71cd54f6c7126f6"
+ }
+}
diff --git a/conformance/fixtures/032-typed-slot-contract/source/agent.yaml b/conformance/fixtures/032-typed-slot-contract/source/agent.yaml
new file mode 100644
index 0000000..d690851
--- /dev/null
+++ b/conformance/fixtures/032-typed-slot-contract/source/agent.yaml
@@ -0,0 +1,5 @@
+schemaVersion: 4
+kind: agent
+id: conformance/agents/typed-slot@1.0.0
+slots:
+ evidence: conformance/context/value@1.0.0
diff --git a/conformance/fixtures/032-typed-slot-contract/source/expected.yaml b/conformance/fixtures/032-typed-slot-contract/source/expected.yaml
new file mode 100644
index 0000000..8233faa
--- /dev/null
+++ b/conformance/fixtures/032-typed-slot-contract/source/expected.yaml
@@ -0,0 +1,3 @@
+schemaVersion: 4
+kind: contextType
+id: conformance/context-types/expected@1.0.0
diff --git a/conformance/fixtures/032-typed-slot-contract/source/interface.yaml b/conformance/fixtures/032-typed-slot-contract/source/interface.yaml
new file mode 100644
index 0000000..5b0ab98
--- /dev/null
+++ b/conformance/fixtures/032-typed-slot-contract/source/interface.yaml
@@ -0,0 +1,5 @@
+schemaVersion: 4
+kind: interface
+id: conformance/interfaces/typed-slot@1.0.0
+requiresSlots:
+ evidence: conformance/context-types/expected@1.0.0
diff --git a/conformance/fixtures/032-typed-slot-contract/source/other.yaml b/conformance/fixtures/032-typed-slot-contract/source/other.yaml
new file mode 100644
index 0000000..dfd1b9a
--- /dev/null
+++ b/conformance/fixtures/032-typed-slot-contract/source/other.yaml
@@ -0,0 +1,3 @@
+schemaVersion: 4
+kind: contextType
+id: conformance/context-types/other@1.0.0
diff --git a/conformance/fixtures/032-typed-slot-contract/source/value.tfer b/conformance/fixtures/032-typed-slot-contract/source/value.tfer
new file mode 100644
index 0000000..bb61403
--- /dev/null
+++ b/conformance/fixtures/032-typed-slot-contract/source/value.tfer
@@ -0,0 +1,6 @@
+---
+schemaVersion: 4
+kind: context
+id: conformance/context/value@1.0.0
+contextType: conformance/context-types/other@1.0.0
+---
diff --git a/conformance/fixtures/033-slot-context-kind-error/manifest.json b/conformance/fixtures/033-slot-context-kind-error/manifest.json
new file mode 100644
index 0000000..c5715cd
--- /dev/null
+++ b/conformance/fixtures/033-slot-context-kind-error/manifest.json
@@ -0,0 +1,4 @@
+{
+ "description": "A version 4 slot value must resolve to a context resource, not merely any valid resource id.",
+ "expect": "error"
+}
diff --git a/conformance/fixtures/033-slot-context-kind-error/source/agent.yaml b/conformance/fixtures/033-slot-context-kind-error/source/agent.yaml
new file mode 100644
index 0000000..118ca35
--- /dev/null
+++ b/conformance/fixtures/033-slot-context-kind-error/source/agent.yaml
@@ -0,0 +1,5 @@
+schemaVersion: 4
+kind: agent
+id: conformance/agents/bad-slot@1.0.0
+slots:
+ evidence: conformance/capabilities/not-context@1.0.0
diff --git a/conformance/fixtures/033-slot-context-kind-error/source/not-context.yaml b/conformance/fixtures/033-slot-context-kind-error/source/not-context.yaml
new file mode 100644
index 0000000..b563698
--- /dev/null
+++ b/conformance/fixtures/033-slot-context-kind-error/source/not-context.yaml
@@ -0,0 +1,3 @@
+schemaVersion: 4
+kind: capability
+id: conformance/capabilities/not-context@1.0.0
diff --git a/conformance/fixtures/034-duplicate-yaml-key-error/manifest.json b/conformance/fixtures/034-duplicate-yaml-key-error/manifest.json
new file mode 100644
index 0000000..f6241e3
--- /dev/null
+++ b/conformance/fixtures/034-duplicate-yaml-key-error/manifest.json
@@ -0,0 +1,4 @@
+{
+ "description": "Duplicate YAML keys are rejected instead of silently selecting one value.",
+ "expect": "error"
+}
diff --git a/conformance/fixtures/034-duplicate-yaml-key-error/source/agent.yaml b/conformance/fixtures/034-duplicate-yaml-key-error/source/agent.yaml
new file mode 100644
index 0000000..5e598de
--- /dev/null
+++ b/conformance/fixtures/034-duplicate-yaml-key-error/source/agent.yaml
@@ -0,0 +1,5 @@
+schemaVersion: 4
+kind: agent
+id: conformance/agents/duplicate-key@1.0.0
+description: first
+description: second
diff --git a/conformance/fixtures/035-target-name-collision-error/manifest.json b/conformance/fixtures/035-target-name-collision-error/manifest.json
new file mode 100644
index 0000000..dd587c2
--- /dev/null
+++ b/conformance/fixtures/035-target-name-collision-error/manifest.json
@@ -0,0 +1,4 @@
+{
+ "description": "Distinct agent IDs that collapse to one target artifact name fail instead of overwriting.",
+ "expect": "error"
+}
diff --git a/conformance/fixtures/035-target-name-collision-error/source/first.yaml b/conformance/fixtures/035-target-name-collision-error/source/first.yaml
new file mode 100644
index 0000000..0a67133
--- /dev/null
+++ b/conformance/fixtures/035-target-name-collision-error/source/first.yaml
@@ -0,0 +1,3 @@
+schemaVersion: 4
+kind: agent
+id: conformance/agents/worker@1.0.0
diff --git a/conformance/fixtures/035-target-name-collision-error/source/second.yaml b/conformance/fixtures/035-target-name-collision-error/source/second.yaml
new file mode 100644
index 0000000..4b002dd
--- /dev/null
+++ b/conformance/fixtures/035-target-name-collision-error/source/second.yaml
@@ -0,0 +1,3 @@
+schemaVersion: 4
+kind: agent
+id: other/agents/worker@1.0.0
diff --git a/conformance/fixtures/036-named-context-value/manifest.json b/conformance/fixtures/036-named-context-value/manifest.json
new file mode 100644
index 0000000..e80725d
--- /dev/null
+++ b/conformance/fixtures/036-named-context-value/manifest.json
@@ -0,0 +1,10 @@
+{
+ "description": "Named context values materialize nested defaults and preserve scalar types.",
+ "expect": "success",
+ "digests": {
+ "codex": "sha256:70c9710e2ae91f28096dc5f1abd18cf6f21d3624ba9ef494421e223e4253329b",
+ "copilot": "sha256:e18c3db63a849c3b10f5997334fab328f47c79772de0d52b8fff417a26fd0036",
+ "cursor": "sha256:c660d0637b68976220455ce8e4c4bdd8b714407a6a1a09dfbca136f9294bdfb0",
+ "neutral": "sha256:18741b98d6418588526686c6148666718ad04e2eb07b06b463c3f1667434d091"
+ }
+}
diff --git a/conformance/fixtures/036-named-context-value/source/agent.yaml b/conformance/fixtures/036-named-context-value/source/agent.yaml
new file mode 100644
index 0000000..4a396d9
--- /dev/null
+++ b/conformance/fixtures/036-named-context-value/source/agent.yaml
@@ -0,0 +1,5 @@
+schemaVersion: 4
+kind: agent
+id: conformance/agents/named-context@1.0.0
+context:
+ - conformance/context/named@1.0.0
diff --git a/conformance/fixtures/036-named-context-value/source/nested.yaml b/conformance/fixtures/036-named-context-value/source/nested.yaml
new file mode 100644
index 0000000..7193cfc
--- /dev/null
+++ b/conformance/fixtures/036-named-context-value/source/nested.yaml
@@ -0,0 +1,10 @@
+schemaVersion: 4
+kind: contextType
+id: conformance/context-types/nested@1.0.0
+fields:
+ label:
+ type: string
+ required: true
+ enabled:
+ type: boolean
+ default: true
diff --git a/conformance/fixtures/036-named-context-value/source/value.yaml b/conformance/fixtures/036-named-context-value/source/value.yaml
new file mode 100644
index 0000000..5d8cb39
--- /dev/null
+++ b/conformance/fixtures/036-named-context-value/source/value.yaml
@@ -0,0 +1,7 @@
+schemaVersion: 4
+kind: context
+id: conformance/context/named@1.0.0
+contextType: conformance/context-types/wrapper@1.0.0
+values:
+ nested:
+ label: ready
diff --git a/conformance/fixtures/036-named-context-value/source/wrapper.yaml b/conformance/fixtures/036-named-context-value/source/wrapper.yaml
new file mode 100644
index 0000000..fc1fce0
--- /dev/null
+++ b/conformance/fixtures/036-named-context-value/source/wrapper.yaml
@@ -0,0 +1,7 @@
+schemaVersion: 4
+kind: contextType
+id: conformance/context-types/wrapper@1.0.0
+fields:
+ nested:
+ type: conformance/context-types/nested@1.0.0
+ required: true
diff --git a/conformance/fixtures/037-legacy-schema-closed-error/manifest.json b/conformance/fixtures/037-legacy-schema-closed-error/manifest.json
new file mode 100644
index 0000000..788acf4
--- /dev/null
+++ b/conformance/fixtures/037-legacy-schema-closed-error/manifest.json
@@ -0,0 +1,4 @@
+{
+ "description": "The normal closed-language path rejects retired schema version 3 input.",
+ "expect": "error"
+}
diff --git a/conformance/fixtures/037-legacy-schema-closed-error/source/agent.yaml b/conformance/fixtures/037-legacy-schema-closed-error/source/agent.yaml
new file mode 100644
index 0000000..dc6c077
--- /dev/null
+++ b/conformance/fixtures/037-legacy-schema-closed-error/source/agent.yaml
@@ -0,0 +1,3 @@
+schemaVersion: 3
+kind: agent
+id: conformance/agents/legacy@1.0.0
diff --git a/dist-maintainer/ard/ai-catalog.json b/dist-maintainer/ard/ai-catalog.json
index 7272ade..bbd9466 100644
--- a/dist-maintainer/ard/ai-catalog.json
+++ b/dist-maintainer/ard/ai-catalog.json
@@ -6,84 +6,114 @@
},
"entries": [
{
- "identifier": "urn:air:typeference.example:typeference:source:maintainer",
- "displayName": "TypeFerence source package: maintainer",
+ "identifier": "urn:air:typeference.example:typeference:source:typeference-maintainer",
+ "displayName": "TypeFerence source package: typeference-maintainer",
"type": "application/vnd.typeference.source-package\u002Bjson",
"description": "Canonical typed source package for validation, audit, and reproducible compilation.",
- "version": "1.0.0",
+ "version": "0.1.0",
"data": {
"schemaVersion": 1,
- "digest": "sha256:c42776752aef743bd24749eca2f06b0da9b27d51b9edfce13984e39e0aa565c2",
+ "digest": "sha256:5c65966e441ae60e7e4eadda8e4794f87ec9dbca736a922f32373b06b5ebc4b2",
"files": [
{
- "path": "agents/typeference-maintainer.agent.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: agent\nid: typeference/typeference-maintainer@0.1.0\ndisplayName: TypeFerence Maintainer\ndescription: The agent that maintains the TypeFerence repository, defined in TypeFerence\u0027s own terms and compiled into this repository\u0027s AGENTS.md.\nembeds:\n - typeference/profiles/spec-conformance@0.1.0\n - typeference/profiles/determinism-guardian@0.1.0\n - typeference/profiles/trust-signing@0.1.0\n - typeference/profiles/contribution-workflow@0.1.0\nslots:\n repositoryMap: context/repository-map.md\ncontextFiles:\n - context/repository-map.md\n"
+ "path": "agents/typeference-maintainer.agent.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: agent\nid: typeference/typeference-maintainer@0.1.0\ndisplayName: TypeFerence Maintainer\ndescription: The agent that maintains the TypeFerence repository, defined in TypeFerence\u0027s own terms and compiled into this repository\u0027s AGENTS.md.\nembeds:\n - typeference/profiles/spec-conformance@0.1.0\n - typeference/profiles/determinism-guardian@0.1.0\n - typeference/profiles/trust-signing@0.1.0\n - typeference/profiles/contribution-workflow@0.1.0\nslots:\n repositoryMap: typeference/context/repository-map@0.1.0\n---\n"
},
{
- "path": "capabilities/audit-drift.capability.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: capability\nid: typeference/capabilities/audit-drift@0.1.0\ndisplayName: Audit Self-Host Drift\ndescription: Recompile the maintainer definition and report drift against the committed artifacts.\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022clean\u0022:{\u0022type\u0022:\u0022boolean\u0022},\u0022driftedFiles\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022clean\u0022,\u0022driftedFiles\u0022]}\u0027\n"
+ "path": "capabilities/audit-drift.capability.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: capability\nid: typeference/capabilities/audit-drift@0.1.0\ndisplayName: Audit Self-Host Drift\ndescription: Recompile the maintainer definition and report drift against the committed artifacts.\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022clean\u0022:{\u0022type\u0022:\u0022boolean\u0022},\u0022driftedFiles\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022clean\u0022,\u0022driftedFiles\u0022]}\u0027\n---\n"
},
{
- "path": "capabilities/verify-conformance.capability.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: capability\nid: typeference/capabilities/verify-conformance@0.1.0\ndisplayName: Verify Conformance\ndescription: Run both implementations against the shared conformance suite and report agreement.\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022passed\u0022:{\u0022type\u0022:\u0022boolean\u0022},\u0022mismatches\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022passed\u0022,\u0022mismatches\u0022]}\u0027\n"
+ "path": "capabilities/verify-conformance.capability.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: capability\nid: typeference/capabilities/verify-conformance@0.1.0\ndisplayName: Verify Conformance\ndescription: Run the determinism suite and report whether the compiler reproduces the committed digests.\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022passed\u0022:{\u0022type\u0022:\u0022boolean\u0022},\u0022mismatches\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022passed\u0022,\u0022mismatches\u0022]}\u0027\n---\n"
},
{
- "path": "context/contribution.md",
- "mediaType": "text/markdown",
- "content": "# Contribution workflow\n\n- Work on feature branches; never rewrite published history; never tag or publish a\n release outside the checklist in \u0060docs/release-checklist.md\u0060.\n- Before any commit: \u0060dotnet test TypeFerence.slnx\u0060, \u0060go test ./...\u0060 (from \u0060go/\u0060),\n and the conformance suite (\u0060make conformance\u0060) all pass. A commit that breaks any\n of them does not land.\n- Design decisions with real tradeoffs \u2014 spec semantics, canonical bytes, trust\n model, dependencies \u2014 are recorded in \u0060docs/decisions/\u0060 as numbered ADRs in the\n same change.\n- Generated artifacts (root \u0060AGENTS.md\u0060, \u0060dist/\u0060) are only ever changed by\n regenerating them from source (\u0060make selfhost\u0060, \u0060typeference build\u0060); hand edits\n to generated files are drift and CI rejects them.\n- Documentation is accurate against the code at the commit that includes it. The\n project describes itself as an experimental reference implementation; no invented\n adoption, users, benchmarks, or endorsements, ever.\n"
+ "path": "context-types/contribution-workflow.contexttype.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: contextType\nid: typeference/context-types/contribution-workflow@0.1.0\ndisplayName: Contribution Workflow\nbody:\n type: text\n required: true\n---\n"
},
{
- "path": "context/determinism.md",
- "mediaType": "text/markdown",
- "content": "# Determinism guarantees\n\nIdentical source must compile to identical bytes \u2014 across repeated builds, across\nplatforms, and across implementations. The guarantee is enforced, not aspirational:\n\n- \u0060conformance/fixtures/\u0060 records the expected \u0060typeference-directory-v1\u0060 digest of\n every emitted target for 25\u002B fixtures. Both implementations run the corpus in CI.\n- The committed \u0060dist/\u0060 tree is the fully materialized reference output for\n \u0060examples/helio\u0060; both implementations byte-compare against it in their own tests.\n- The committed root \u0060AGENTS.md\u0060 and \u0060dist-maintainer/\u0060 are build artifacts of\n \u0060agents/maintainer/\u0060; CI recompiles the definition and fails on any drift.\n\nRules that protect the guarantee:\n\n- Digest values are regenerated (\u0060go test ./conformance -update\u0060, then verified by\n the C# runner), never typed by hand.\n- Canonical serialization is defined in \u0060docs/specification.md\u0060 (\u0022Deterministic\n compilation\u0022); any change to it is a specification change with an ADR.\n- Nothing about determinism, provenance, or fail-closed behavior is ever relaxed to\n make an unrelated change easier. If a change fights the determinism rules, the\n change is wrong or the specification needs a recorded amendment.\n"
+ "path": "context-types/determinism.contexttype.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: contextType\nid: typeference/context-types/determinism@0.1.0\ndisplayName: Determinism Guarantees\nbody:\n type: text\n required: true\n---\n"
},
{
- "path": "context/repository-map.md",
- "mediaType": "text/markdown",
- "content": "# Repository map\n\n| Path | Role |\n| --- | --- |\n| \u0060docs/specification.md\u0060 | Normative specification (source of truth). |\n| \u0060docs/whitepaper.md\u0060 | Motivation and design narrative. |\n| \u0060docs/decisions/\u0060 | Architecture decision records. |\n| \u0060src/\u0060 | C# reference implementation (.NET 10). |\n| \u0060go/\u0060 | Go implementation (static binary; module \u0060github.com/buchk/TypeFerence/go\u0060). |\n| \u0060tests/\u0060 | C# test suite, including the conformance runner. |\n| \u0060go/conformance/\u0060 | Go conformance runner (\u0060-update\u0060 regenerates digests). |\n| \u0060conformance/\u0060 | Shared cross-implementation fixture corpus. |\n| \u0060examples/helio\u0060 | Example organization used by tests and the quick start. |\n| \u0060dist/\u0060 | Committed reference output of \u0060examples/helio\u0060 (byte-compared in CI). |\n| \u0060agents/maintainer/\u0060 | This definition; compiled into the root \u0060AGENTS.md\u0060 and \u0060dist-maintainer/\u0060. |\n| \u0060.github/workflows/ci.yml\u0060 | Build, test, conformance, and self-host drift gates. |\n\nThe root \u0060AGENTS.md\u0060 is generated from this definition. To change it, edit the\nresources under \u0060agents/maintainer/\u0060 and run \u0060make selfhost\u0060; never edit the\ngenerated file directly.\n"
+ "path": "context-types/repository-map.contexttype.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: contextType\nid: typeference/context-types/repository-map@0.1.0\ndisplayName: Repository Map\ndescription: A stable map of repository paths and their maintenance roles.\nbody:\n type: text\n required: true\n---\n"
},
{
- "path": "context/spec-workflow.md",
- "mediaType": "text/markdown",
- "content": "# Specification-first workflow\n\nThe normative specification is \u0060docs/specification.md\u0060 at the repository root. It is\nthe source of truth for both implementations:\n\n- C# reference implementation: \u0060src/TypeFerence.Core\u0060, \u0060src/TypeFerence.Cli\u0060\n- Go implementation: \u0060go/\u0060\n\nA change is *semantic* when it alters what source trees are valid, how composition\nresolves, or what bytes compilation emits. Semantic changes follow this order, in one\nreviewable change set:\n\n1. Amend \u0060docs/specification.md\u0060.\n2. Record the decision and rejected alternatives as an ADR in \u0060docs/decisions/\u0060.\n3. Add or update fixtures in \u0060conformance/fixtures/\u0060 capturing the ruling.\n4. Update both implementations until the conformance suite passes on both.\n\nIf an implementation is found to disagree with the specification, the specification\nwins. If the specification is ambiguous enough that two honest implementations could\ndiverge, that is a specification bug: fix the text, do not encode a private\ninterpretation in code.\n"
+ "path": "context-types/spec-workflow.contexttype.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: contextType\nid: typeference/context-types/spec-workflow@0.1.0\ndisplayName: Specification Workflow\nbody:\n type: text\n required: true\n---\n"
},
{
- "path": "context/trust-model.md",
- "mediaType": "text/markdown",
- "content": "# Trust model invariants\n\nThe trust model is declarative supply-chain metadata, specified in\n\u0060docs/specification.md\u0060 (\u0022Trust metadata compilation\u0022 and \u0022Trust Manifest\nsignatures\u0022). The invariants that must survive every change:\n\n- **The signature map lives outside the source root.** The source-package digest\n covers every file under the source root; a signature stored inside would change\n the digest embedded in the very payload being signed. Both implementations reject\n a signature map path beneath the source root.\n- **Fail-closed stays fail-closed.** When \u0060signatureIntent.required\u0060 is true and no\n signature is imported, publication fails. \u0060--allow-unsigned-trust\u0060 exists solely\n to emit the payload for an external signer, and its output is visibly unsigned.\n- **TypeFerence never signs.** It imports externally produced compact detached JWS\n strings, validates their shape, and never verifies cryptographic validity,\n resolves keys, or dereferences any trust URI.\n- **Identities are ASCII** (punycode for internationalized authorities) so publisher\n alignment does not depend on implementation-specific IDN handling.\n\nChanges that touch any of these need a specification amendment, an ADR, and\nconformance fixtures before implementation.\n"
+ "path": "context-types/trust-model.contexttype.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: contextType\nid: typeference/context-types/trust-model@0.1.0\ndisplayName: Trust Model\nbody:\n type: text\n required: true\n---\n"
},
{
- "path": "profiles/contribution-workflow.profile.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: profile\nid: typeference/profiles/contribution-workflow@0.1.0\ndisplayName: Contribution Workflow\ndescription: How changes to this repository are made, reviewed, and recorded.\nworkingNorms:\n - Every commit builds and passes both implementations\u0027 test suites and the shared conformance suite.\n - Decisions with real tradeoffs are recorded as ADRs in docs/decisions before the change merges.\n - Documentation must be accurate against the code at the commit that includes it; no fabricated adoption, benchmarks, or endorsements.\n - Commit messages are conventional and written for a critical human reader.\ncontextFiles:\n - context/contribution.md\n"
+ "path": "context/contribution.context.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: context\nid: typeference/context/contribution-workflow@0.1.0\ncontextType: typeference/context-types/contribution-workflow@0.1.0\ndisplayName: Contribution Workflow\n---\n# Contribution workflow\n\n- Work on feature branches; never rewrite published history; never tag or publish a\n release outside the checklist in \u0060docs/release-checklist.md\u0060.\n- Before any commit: \u0060go test ./...\u0060 (from \u0060go/\u0060) and the determinism suite\n (\u0060make conformance\u0060) both pass. A commit that breaks either does not land.\n- Design decisions with real tradeoffs \u2014 spec semantics, canonical bytes, trust\n model, dependencies \u2014 are recorded in \u0060docs/decisions/\u0060 as numbered ADRs in the\n same change.\n- Generated artifacts (root \u0060AGENTS.md\u0060, \u0060dist/\u0060) are only ever changed by\n regenerating them from source (\u0060make selfhost\u0060, \u0060typeference build\u0060); hand edits\n to generated files are drift and CI rejects them.\n- Documentation is accurate against the code at the commit that includes it. The\n project describes itself as an experimental reference implementation; no invented\n adoption, users, benchmarks, or endorsements, ever.\n"
},
{
- "path": "profiles/determinism-guardian.profile.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: profile\nid: typeference/profiles/determinism-guardian@0.1.0\ndisplayName: Determinism Guardian\ndescription: Any codegen change must preserve byte-identical output across both implementations.\nworkingNorms:\n - Any change to code generation must keep the C# and Go implementations byte-identical, verified by the conformance suite before merge.\n - Never weaken determinism, provenance, or fail-closed behavior to make a change easier.\n - A conformance digest is regenerated only together with the specification change and ADR that justify it; never hand-edit a digest.\n - Repeated builds from identical source must stay byte-identical on every platform.\ncontextFiles:\n - context/determinism.md\nskills:\n - ref: typeference/skills/verify-conformance@0.1.0\n - ref: typeference/skills/audit-drift@0.1.0\n"
+ "path": "context/determinism.context.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: context\nid: typeference/context/determinism@0.1.0\ncontextType: typeference/context-types/determinism@0.1.0\ndisplayName: Determinism Guarantees\n---\n# Determinism guarantees\n\nIdentical source must compile to identical bytes \u2014 across repeated builds and\nacross platforms. The guarantee is enforced, not aspirational:\n\n- \u0060conformance/fixtures/\u0060 records the expected \u0060typeference-directory-v1\u0060 digest of\n every emitted target for the fixture corpus; the compiler must reproduce them in\n CI (a golden-file determinism suite).\n- The committed \u0060dist/\u0060 tree is the fully materialized reference output for\n \u0060examples/helio\u0060; the compiler byte-compares against it in its tests.\n- The committed root \u0060AGENTS.md\u0060 and \u0060dist-maintainer/\u0060 are build artifacts of\n \u0060agents/maintainer/\u0060; CI recompiles the definition and fails on any drift.\n\nRules that protect the guarantee:\n\n- Digest values are regenerated (\u0060go test ./conformance -update\u0060), never typed by\n hand.\n- Canonical serialization is defined in \u0060docs/specification.md\u0060 (\u0022Canonicalization\u0022);\n any change to it is a specification change with an ADR.\n- Nothing about determinism, provenance, or fail-closed behavior is ever relaxed to\n make an unrelated change easier. If a change fights the determinism rules, the\n change is wrong or the specification needs a recorded amendment.\n"
},
{
- "path": "profiles/spec-conformance.profile.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: profile\nid: typeference/profiles/spec-conformance@0.1.0\ndisplayName: Spec Conformance\ndescription: Semantic changes land in the specification first, then in both implementations, with conformance fixtures.\nworkingNorms:\n - Semantic changes land in docs/specification.md before either implementation changes behavior.\n - Where the specification and an implementation disagree, the specification wins; record the ruling in docs/decisions and cover it with a conformance fixture.\n - Every canonicalization or composition ruling ships with a fixture under conformance/fixtures in the same change.\n - Never silently diverge from the specification; fixing the specification is allowed, silent divergence is not.\ncontextFiles:\n - context/spec-workflow.md\n"
+ "path": "context/repository-map.context.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: context\nid: typeference/context/repository-map@0.1.0\ncontextType: typeference/context-types/repository-map@0.1.0\ndisplayName: Repository Map\n---\n# Repository map\n\n| Path | Role |\n| --- | --- |\n| \u0060docs/specification.md\u0060 | Normative specification (source of truth). |\n| \u0060docs/whitepaper.md\u0060 | Motivation and design narrative. |\n| \u0060docs/decisions/\u0060 | Architecture decision records. |\n| \u0060go/\u0060 | The implementation (static binary; module \u0060github.com/buchk/TypeFerence/go\u0060). |\n| \u0060go/cmd/typeference-lsp/\u0060 | Language server for \u0060.tfer\u0060/\u0060.yaml\u0060 authoring. |\n| \u0060editors/vscode/\u0060 | VS Code client for the language server. |\n| \u0060go/conformance/\u0060 | Determinism runner (\u0060-update\u0060 regenerates digests). |\n| \u0060conformance/\u0060 | Golden-file fixture corpus. |\n| \u0060examples/helio\u0060 | Example organization used by tests and the quick start. |\n| \u0060dist/\u0060 | Committed reference output of \u0060examples/helio\u0060 (byte-compared in CI). |\n| \u0060agents/maintainer/\u0060 | This definition; compiled into the root \u0060AGENTS.md\u0060 and \u0060dist-maintainer/\u0060. |\n| \u0060.github/workflows/ci.yml\u0060 | Build, test, determinism, and self-host drift gates. |\n\nThe root \u0060AGENTS.md\u0060 is generated from this definition. To change it, edit the\nresources under \u0060agents/maintainer/\u0060 and run \u0060make selfhost\u0060; never edit the\ngenerated file directly.\n"
},
{
- "path": "profiles/trust-signing.profile.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: profile\nid: typeference/profiles/trust-signing@0.1.0\ndisplayName: Trust and Signing\ndescription: Preserves the supply-chain trust model exactly as specified.\nworkingNorms:\n - The signature map always resides outside the source root; moving it inside creates a digest/signature cycle and is forbidden.\n - signatureIntent.required fails closed; the unsigned-staging option exists solely to emit payloads for an external signer.\n - TypeFerence imports externally produced signatures; it never signs, never verifies cryptographic validity, and never resolves keys.\n - Trust metadata is declarative; never dereference identity, attestation, or provenance URIs during compilation.\ncontextFiles:\n - context/trust-model.md\n"
+ "path": "context/spec-workflow.context.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: context\nid: typeference/context/spec-workflow@0.1.0\ncontextType: typeference/context-types/spec-workflow@0.1.0\ndisplayName: Specification-first Workflow\n---\n# Specification-first workflow\n\nThe normative specification is \u0060docs/specification.md\u0060 at the repository root. It is\nthe source of truth; the Go implementation under \u0060go/\u0060 is its reference realization.\n\nA semantic change alters valid source, composition, or compiled bytes. It follows:\n\n1. Amend \u0060docs/specification.md\u0060.\n2. Record the decision and rejected alternatives in \u0060docs/decisions/\u0060.\n3. Add or update \u0060conformance/fixtures/\u0060.\n4. Update the implementation until determinism passes.\n\nIf implementation and specification disagree, the specification wins. Ambiguity is\na specification bug, not permission for a private implementation rule.\n"
},
{
- "path": "skills/audit-drift.skill.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: skill\nid: typeference/skills/audit-drift@0.1.0\nbinds: typeference/capabilities/audit-drift@0.1.0\ndisplayName: Audit Self-Host Drift Skill\ndescription: Confirms the committed AGENTS.md and maintainer bundle are exact build artifacts of this definition.\ninstructions: |\n Run \u0060typeference diff agents/maintainer --against dist-maintainer --target neutral\n --emit-ard --publisher-domain typeference.example\u0060 from the repository root, then\n byte-compare the repository-root AGENTS.md against\n dist-maintainer/neutral/typeference-maintainer/AGENTS.md. Report clean=true only\n when the diff exits 0 and the byte comparison matches. Any drift between the\n definition and its committed artifacts is a broken build: regenerate with\n \u0060make selfhost\u0060 and commit definition and artifacts together, or revert the\n stray edit to the generated files.\ncontextFiles:\n - context/determinism.md\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022clean\u0022:{\u0022type\u0022:\u0022boolean\u0022},\u0022driftedFiles\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022clean\u0022,\u0022driftedFiles\u0022]}\u0027\n"
+ "path": "context/trust-model.context.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: context\nid: typeference/context/trust-model@0.1.0\ncontextType: typeference/context-types/trust-model@0.1.0\ndisplayName: Trust Model\n---\n# Trust model invariants\n\n- The signature map lives outside the source root, avoiding a digest/signature cycle.\n- \u0060signatureIntent.required\u0060 fails closed; unsigned staging only emits payloads for\n an external signer.\n- TypeFerence imports externally produced signatures; it never signs, verifies\n cryptographic validity, resolves keys, or dereferences trust URIs.\n- Identities are ASCII; internationalized authorities use punycode.\n\nTrust changes require a specification amendment, ADR, and conformance fixtures.\n"
},
{
- "path": "skills/verify-conformance.skill.yaml",
+ "path": "profiles/contribution-workflow.profile.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: profile\nid: typeference/profiles/contribution-workflow@0.1.0\ndisplayName: Contribution Workflow\ndescription: How changes to this repository are made, reviewed, and recorded.\nworkingNorms:\n - Every commit builds and passes the test suite and the determinism suite.\n - Decisions with real tradeoffs are recorded as ADRs in docs/decisions before the change merges.\n - Documentation must be accurate against the code at the commit that includes it; no fabricated adoption, benchmarks, or endorsements.\n - Commit messages are conventional and written for a critical human reader.\ncontext:\n - typeference/context/contribution-workflow@0.1.0\n---\n"
+ },
+ {
+ "path": "profiles/determinism-guardian.profile.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: profile\nid: typeference/profiles/determinism-guardian@0.1.0\ndisplayName: Determinism Guardian\ndescription: Any codegen change must preserve byte-identical output, verified by the determinism suite.\nworkingNorms:\n - Any change to code generation must reproduce the committed digests, verified by the determinism suite before merge.\n - Never weaken determinism, provenance, or fail-closed behavior to make a change easier.\n - A conformance digest is regenerated only together with the specification change and ADR that justify it; never hand-edit a digest.\n - Repeated builds from identical source must stay byte-identical on every platform.\ncontext:\n - typeference/context/determinism@0.1.0\nskills:\n - ref: typeference/skills/verify-conformance@0.1.0\n - ref: typeference/skills/audit-drift@0.1.0\n---\n"
+ },
+ {
+ "path": "profiles/spec-conformance.profile.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: profile\nid: typeference/profiles/spec-conformance@0.1.0\ndisplayName: Spec Conformance\ndescription: Semantic changes land in the specification first, then in the implementation, with conformance fixtures.\nworkingNorms:\n - Semantic changes land in docs/specification.md before either implementation changes behavior.\n - Where the specification and an implementation disagree, the specification wins; record the ruling in docs/decisions and cover it with a conformance fixture.\n - Every canonicalization or composition ruling ships with a fixture under conformance/fixtures in the same change.\n - Never silently diverge from the specification; fixing the specification is allowed, silent divergence is not.\ncontext:\n - typeference/context/spec-workflow@0.1.0\n---\n"
+ },
+ {
+ "path": "profiles/trust-signing.profile.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: profile\nid: typeference/profiles/trust-signing@0.1.0\ndisplayName: Trust and Signing\ndescription: Preserves the supply-chain trust model exactly as specified.\nworkingNorms:\n - The signature map always resides outside the source root; moving it inside creates a digest/signature cycle and is forbidden.\n - signatureIntent.required fails closed; the unsigned-staging option exists solely to emit payloads for an external signer.\n - TypeFerence imports externally produced signatures; it never signs, never verifies cryptographic validity, and never resolves keys.\n - Trust metadata is declarative; never dereference identity, attestation, or provenance URIs during compilation.\ncontext:\n - typeference/context/trust-model@0.1.0\n---\n"
+ },
+ {
+ "path": "skills/audit-drift.skill.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: skill\nid: typeference/skills/audit-drift@0.1.0\nbinds: typeference/capabilities/audit-drift@0.1.0\ndisplayName: Audit Self-Host Drift Skill\ndescription: Confirms the committed AGENTS.md and maintainer bundle are exact build artifacts of this definition.\ninstructions: |\n Run \u0060typeference diff agents/maintainer --against dist-maintainer --target neutral\n --emit-ard --publisher-domain typeference.example\u0060 from the repository root, then\n byte-compare the repository-root AGENTS.md against\n dist-maintainer/neutral/typeference-maintainer/AGENTS.md. Report clean=true only\n when the diff exits 0 and the byte comparison matches. Any drift between the\n definition and its committed artifacts is a broken build: regenerate with\n \u0060make selfhost\u0060 and commit definition and artifacts together, or revert the\n stray edit to the generated files.\nrequiresContextTypes:\n - typeference/context-types/determinism@0.1.0\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022clean\u0022:{\u0022type\u0022:\u0022boolean\u0022},\u0022driftedFiles\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022clean\u0022,\u0022driftedFiles\u0022]}\u0027\n---\n"
+ },
+ {
+ "path": "skills/verify-conformance.skill.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: skill\nid: typeference/skills/verify-conformance@0.1.0\nbinds: typeference/capabilities/verify-conformance@0.1.0\ndisplayName: Verify Conformance Skill\ndescription: Runs the determinism suite and reports whether the compiler reproduces the committed digests.\ninstructions: |\n Run \u0060go test ./conformance\u0060 from the go/ directory (or \u0060make conformance\u0060).\n Report passed=true only when every fixture reproduces its committed digest.\n List each failing fixture and target as a mismatch. Never resolve a mismatch\n by editing a digest; find the compiler regression, or take the ruling to the\n specification with an ADR and a regenerated fixture.\nrequiresContextTypes:\n - typeference/context-types/determinism@0.1.0\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022passed\u0022:{\u0022type\u0022:\u0022boolean\u0022},\u0022mismatches\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022passed\u0022,\u0022mismatches\u0022]}\u0027\n---\n"
+ },
+ {
+ "path": "typeference.yaml",
"mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: skill\nid: typeference/skills/verify-conformance@0.1.0\nbinds: typeference/capabilities/verify-conformance@0.1.0\ndisplayName: Verify Conformance Skill\ndescription: Runs the shared conformance suite on both implementations and reports any digest disagreement.\ninstructions: |\n Run \u0060go test ./conformance\u0060 from the go/ directory, then run\n \u0060dotnet test TypeFerence.slnx --filter FullyQualifiedName~ConformanceSuiteTests\u0060\n from the repository root (or \u0060make conformance\u0060 for both). Report passed=true\n only when every fixture passes on both implementations. List each failing\n fixture and target as a mismatch. Never resolve a mismatch by editing a\n digest; find the diverging implementation or take the ruling to the\n specification with an ADR and a new fixture.\ncontextFiles:\n - context/determinism.md\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022passed\u0022:{\u0022type\u0022:\u0022boolean\u0022},\u0022mismatches\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022passed\u0022,\u0022mismatches\u0022]}\u0027\n"
+ "content": "schemaVersion: 2\nname: typeference/maintainer\nversion: 0.1.0\npublisher: typeference.example\n"
}
]
},
@@ -107,37 +137,42 @@
"target": "neutral",
"agentId": "typeference/typeference-maintainer@0.1.0",
"files": [
+ {
+ "path": ".typeference/link.json",
+ "mediaType": "application/json",
+ "content": "{\n \u0022schemaVersion\u0022: 1,\n \u0022agentId\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022,\n \u0022target\u0022: \u0022neutral\u0022,\n \u0022defaultMode\u0022: \u0022\u0022,\n \u0022sourceDigest\u0022: \u0022sha256:5c65966e441ae60e7e4eadda8e4794f87ec9dbca736a922f32373b06b5ebc4b2\u0022,\n \u0022dependencies\u0022: [],\n \u0022modes\u0022: [],\n \u0022toolImports\u0022: []\n}\n"
+ },
{
"path": "AGENTS.md",
"mediaType": "text/markdown",
- "content": "# TypeFerence Maintainer\n\nThe agent that maintains the TypeFerence repository, defined in TypeFerence\u0027s own terms and compiled into this repository\u0027s AGENTS.md.\n\n## Working norms\n\n- Semantic changes land in docs/specification.md before either implementation changes behavior.\n- Where the specification and an implementation disagree, the specification wins; record the ruling in docs/decisions and cover it with a conformance fixture.\n- Every canonicalization or composition ruling ships with a fixture under conformance/fixtures in the same change.\n- Never silently diverge from the specification; fixing the specification is allowed, silent divergence is not.\n- Any change to code generation must keep the C# and Go implementations byte-identical, verified by the conformance suite before merge.\n- Never weaken determinism, provenance, or fail-closed behavior to make a change easier.\n- A conformance digest is regenerated only together with the specification change and ADR that justify it; never hand-edit a digest.\n- Repeated builds from identical source must stay byte-identical on every platform.\n- The signature map always resides outside the source root; moving it inside creates a digest/signature cycle and is forbidden.\n- signatureIntent.required fails closed; the unsigned-staging option exists solely to emit payloads for an external signer.\n- TypeFerence imports externally produced signatures; it never signs, never verifies cryptographic validity, and never resolves keys.\n- Trust metadata is declarative; never dereference identity, attestation, or provenance URIs during compilation.\n- Every commit builds and passes both implementations\u0027 test suites and the shared conformance suite.\n- Decisions with real tradeoffs are recorded as ADRs in docs/decisions before the change merges.\n- Documentation must be accurate against the code at the commit that includes it; no fabricated adoption, benchmarks, or endorsements.\n- Commit messages are conventional and written for a critical human reader.\n\n## Context slots\n\n- \u0060repositoryMap\u0060: \u0060context/repository-map.md\u0060\n\n## Available skills\n\n- \u0060typeference-maintainer.audit-drift\u0060: Confirms the committed AGENTS.md and maintainer bundle are exact build artifacts of this definition.\n- \u0060typeference-maintainer.verify-conformance\u0060: Runs the shared conformance suite on both implementations and reports any digest disagreement.\n\n"
+ "content": "# TypeFerence Maintainer\n\nThe agent that maintains the TypeFerence repository, defined in TypeFerence\u0027s own terms and compiled into this repository\u0027s AGENTS.md.\n\n## Working norms\n\n- Semantic changes land in docs/specification.md before either implementation changes behavior.\n- Where the specification and an implementation disagree, the specification wins; record the ruling in docs/decisions and cover it with a conformance fixture.\n- Every canonicalization or composition ruling ships with a fixture under conformance/fixtures in the same change.\n- Never silently diverge from the specification; fixing the specification is allowed, silent divergence is not.\n- Any change to code generation must reproduce the committed digests, verified by the determinism suite before merge.\n- Never weaken determinism, provenance, or fail-closed behavior to make a change easier.\n- A conformance digest is regenerated only together with the specification change and ADR that justify it; never hand-edit a digest.\n- Repeated builds from identical source must stay byte-identical on every platform.\n- The signature map always resides outside the source root; moving it inside creates a digest/signature cycle and is forbidden.\n- signatureIntent.required fails closed; the unsigned-staging option exists solely to emit payloads for an external signer.\n- TypeFerence imports externally produced signatures; it never signs, never verifies cryptographic validity, and never resolves keys.\n- Trust metadata is declarative; never dereference identity, attestation, or provenance URIs during compilation.\n- Every commit builds and passes the test suite and the determinism suite.\n- Decisions with real tradeoffs are recorded as ADRs in docs/decisions before the change merges.\n- Documentation must be accurate against the code at the commit that includes it; no fabricated adoption, benchmarks, or endorsements.\n- Commit messages are conventional and written for a critical human reader.\n\n## Context slots\n\n- \u0060repositoryMap\u0060: \u0060typeference/context/repository-map@0.1.0\u0060\n\n## Context\n\n### Contribution Workflow\n\n# Contribution workflow\n\n- Work on feature branches; never rewrite published history; never tag or publish a\n release outside the checklist in \u0060docs/release-checklist.md\u0060.\n- Before any commit: \u0060go test ./...\u0060 (from \u0060go/\u0060) and the determinism suite\n (\u0060make conformance\u0060) both pass. A commit that breaks either does not land.\n- Design decisions with real tradeoffs \u2014 spec semantics, canonical bytes, trust\n model, dependencies \u2014 are recorded in \u0060docs/decisions/\u0060 as numbered ADRs in the\n same change.\n- Generated artifacts (root \u0060AGENTS.md\u0060, \u0060dist/\u0060) are only ever changed by\n regenerating them from source (\u0060make selfhost\u0060, \u0060typeference build\u0060); hand edits\n to generated files are drift and CI rejects them.\n- Documentation is accurate against the code at the commit that includes it. The\n project describes itself as an experimental reference implementation; no invented\n adoption, users, benchmarks, or endorsements, ever.\n\n### Determinism Guarantees\n\n# Determinism guarantees\n\nIdentical source must compile to identical bytes \u2014 across repeated builds and\nacross platforms. The guarantee is enforced, not aspirational:\n\n- \u0060conformance/fixtures/\u0060 records the expected \u0060typeference-directory-v1\u0060 digest of\n every emitted target for the fixture corpus; the compiler must reproduce them in\n CI (a golden-file determinism suite).\n- The committed \u0060dist/\u0060 tree is the fully materialized reference output for\n \u0060examples/helio\u0060; the compiler byte-compares against it in its tests.\n- The committed root \u0060AGENTS.md\u0060 and \u0060dist-maintainer/\u0060 are build artifacts of\n \u0060agents/maintainer/\u0060; CI recompiles the definition and fails on any drift.\n\nRules that protect the guarantee:\n\n- Digest values are regenerated (\u0060go test ./conformance -update\u0060), never typed by\n hand.\n- Canonical serialization is defined in \u0060docs/specification.md\u0060 (\u0022Canonicalization\u0022);\n any change to it is a specification change with an ADR.\n- Nothing about determinism, provenance, or fail-closed behavior is ever relaxed to\n make an unrelated change easier. If a change fights the determinism rules, the\n change is wrong or the specification needs a recorded amendment.\n\n### Repository Map\n\n# Repository map\n\n| Path | Role |\n| --- | --- |\n| \u0060docs/specification.md\u0060 | Normative specification (source of truth). |\n| \u0060docs/whitepaper.md\u0060 | Motivation and design narrative. |\n| \u0060docs/decisions/\u0060 | Architecture decision records. |\n| \u0060go/\u0060 | The implementation (static binary; module \u0060github.com/buchk/TypeFerence/go\u0060). |\n| \u0060go/cmd/typeference-lsp/\u0060 | Language server for \u0060.tfer\u0060/\u0060.yaml\u0060 authoring. |\n| \u0060editors/vscode/\u0060 | VS Code client for the language server. |\n| \u0060go/conformance/\u0060 | Determinism runner (\u0060-update\u0060 regenerates digests). |\n| \u0060conformance/\u0060 | Golden-file fixture corpus. |\n| \u0060examples/helio\u0060 | Example organization used by tests and the quick start. |\n| \u0060dist/\u0060 | Committed reference output of \u0060examples/helio\u0060 (byte-compared in CI). |\n| \u0060agents/maintainer/\u0060 | This definition; compiled into the root \u0060AGENTS.md\u0060 and \u0060dist-maintainer/\u0060. |\n| \u0060.github/workflows/ci.yml\u0060 | Build, test, determinism, and self-host drift gates. |\n\nThe root \u0060AGENTS.md\u0060 is generated from this definition. To change it, edit the\nresources under \u0060agents/maintainer/\u0060 and run \u0060make selfhost\u0060; never edit the\ngenerated file directly.\n\n### Specification-first Workflow\n\n# Specification-first workflow\n\nThe normative specification is \u0060docs/specification.md\u0060 at the repository root. It is\nthe source of truth; the Go implementation under \u0060go/\u0060 is its reference realization.\n\nA semantic change alters valid source, composition, or compiled bytes. It follows:\n\n1. Amend \u0060docs/specification.md\u0060.\n2. Record the decision and rejected alternatives in \u0060docs/decisions/\u0060.\n3. Add or update \u0060conformance/fixtures/\u0060.\n4. Update the implementation until determinism passes.\n\nIf implementation and specification disagree, the specification wins. Ambiguity is\na specification bug, not permission for a private implementation rule.\n\n### Trust Model\n\n# Trust model invariants\n\n- The signature map lives outside the source root, avoiding a digest/signature cycle.\n- \u0060signatureIntent.required\u0060 fails closed; unsigned staging only emits payloads for\n an external signer.\n- TypeFerence imports externally produced signatures; it never signs, verifies\n cryptographic validity, resolves keys, or dereferences trust URIs.\n- Identities are ASCII; internationalized authorities use punycode.\n\nTrust changes require a specification amendment, ADR, and conformance fixtures.\n\n## Available skills\n\n- \u0060typeference-maintainer.audit-drift\u0060: Confirms the committed AGENTS.md and maintainer bundle are exact build artifacts of this definition.\n- \u0060typeference-maintainer.verify-conformance\u0060: Runs the determinism suite and reports whether the compiler reproduces the committed digests.\n\n"
},
{
"path": "bundle.json",
"mediaType": "application/json",
- "content": "{\n \u0022id\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022,\n \u0022displayName\u0022: \u0022TypeFerence Maintainer\u0022,\n \u0022description\u0022: \u0022The agent that maintains the TypeFerence repository, defined in TypeFerence\\u0027s own terms and compiled into this repository\\u0027s AGENTS.md.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022typeference/profiles/spec-conformance@0.1.0\u0022,\n \u0022typeference/profiles/determinism-guardian@0.1.0\u0022,\n \u0022typeference/profiles/trust-signing@0.1.0\u0022,\n \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n ],\n \u0022satisfies\u0022: [],\n \u0022slots\u0022: {\n \u0022repositoryMap\u0022: \u0022context/repository-map.md\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Semantic changes land in docs/specification.md before either implementation changes behavior.\u0022,\n \u0022Where the specification and an implementation disagree, the specification wins; record the ruling in docs/decisions and cover it with a conformance fixture.\u0022,\n \u0022Every canonicalization or composition ruling ships with a fixture under conformance/fixtures in the same change.\u0022,\n \u0022Never silently diverge from the specification; fixing the specification is allowed, silent divergence is not.\u0022,\n \u0022Any change to code generation must keep the C# and Go implementations byte-identical, verified by the conformance suite before merge.\u0022,\n \u0022Never weaken determinism, provenance, or fail-closed behavior to make a change easier.\u0022,\n \u0022A conformance digest is regenerated only together with the specification change and ADR that justify it; never hand-edit a digest.\u0022,\n \u0022Repeated builds from identical source must stay byte-identical on every platform.\u0022,\n \u0022The signature map always resides outside the source root; moving it inside creates a digest/signature cycle and is forbidden.\u0022,\n \u0022signatureIntent.required fails closed; the unsigned-staging option exists solely to emit payloads for an external signer.\u0022,\n \u0022TypeFerence imports externally produced signatures; it never signs, never verifies cryptographic validity, and never resolves keys.\u0022,\n \u0022Trust metadata is declarative; never dereference identity, attestation, or provenance URIs during compilation.\u0022,\n \u0022Every commit builds and passes both implementations\\u0027 test suites and the shared conformance suite.\u0022,\n \u0022Decisions with real tradeoffs are recorded as ADRs in docs/decisions before the change merges.\u0022,\n \u0022Documentation must be accurate against the code at the commit that includes it; no fabricated adoption, benchmarks, or endorsements.\u0022,\n \u0022Commit messages are conventional and written for a critical human reader.\u0022\n ],\n \u0022contextFiles\u0022: [\n \u0022context/spec-workflow.md\u0022,\n \u0022context/determinism.md\u0022,\n \u0022context/trust-model.md\u0022,\n \u0022context/contribution.md\u0022,\n \u0022context/repository-map.md\u0022\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022typeference-maintainer.audit-drift\u0022,\n \u0022capabilityId\u0022: \u0022typeference/capabilities/audit-drift@0.1.0\u0022,\n \u0022implementationId\u0022: \u0022typeference/skills/audit-drift@0.1.0\u0022,\n \u0022description\u0022: \u0022Confirms the committed AGENTS.md and maintainer bundle are exact build artifacts of this definition.\u0022,\n \u0022instructions\u0022: \u0022Run \\u0060typeference diff agents/maintainer --against dist-maintainer --target neutral\\n--emit-ard --publisher-domain typeference.example\\u0060 from the repository root, then\\nbyte-compare the repository-root AGENTS.md against\\ndist-maintainer/neutral/typeference-maintainer/AGENTS.md. Report clean=true only\\nwhen the diff exits 0 and the byte comparison matches. Any drift between the\\ndefinition and its committed artifacts is a broken build: regenerate with\\n\\u0060make selfhost\\u0060 and commit definition and artifacts together, or revert the\\nstray edit to the generated files.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022clean\\u0022:{\\u0022type\\u0022:\\u0022boolean\\u0022},\\u0022driftedFiles\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022clean\\u0022,\\u0022driftedFiles\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/determinism.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022typeference/capabilities/audit-drift@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022typeference/skills/audit-drift@0.1.0\u0022\n }\n ]\n },\n {\n \u0022dispatchName\u0022: \u0022typeference-maintainer.verify-conformance\u0022,\n \u0022capabilityId\u0022: \u0022typeference/capabilities/verify-conformance@0.1.0\u0022,\n \u0022implementationId\u0022: \u0022typeference/skills/verify-conformance@0.1.0\u0022,\n \u0022description\u0022: \u0022Runs the shared conformance suite on both implementations and reports any digest disagreement.\u0022,\n \u0022instructions\u0022: \u0022Run \\u0060go test ./conformance\\u0060 from the go/ directory, then run\\n\\u0060dotnet test TypeFerence.slnx --filter FullyQualifiedName~ConformanceSuiteTests\\u0060\\nfrom the repository root (or \\u0060make conformance\\u0060 for both). Report passed=true\\nonly when every fixture passes on both implementations. List each failing\\nfixture and target as a mismatch. Never resolve a mismatch by editing a\\ndigest; find the diverging implementation or take the ruling to the\\nspecification with an ADR and a new fixture.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022passed\\u0022:{\\u0022type\\u0022:\\u0022boolean\\u0022},\\u0022mismatches\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022passed\\u0022,\\u0022mismatches\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/determinism.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022typeference/capabilities/verify-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022typeference/skills/verify-conformance@0.1.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/spec-conformance@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/determinism-guardian@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/trust-signing@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/contribution-workflow@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repositoryMap\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n }\n ]\n}\n"
+ "content": "{\n \u0022id\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022,\n \u0022displayName\u0022: \u0022TypeFerence Maintainer\u0022,\n \u0022description\u0022: \u0022The agent that maintains the TypeFerence repository, defined in TypeFerence\\u0027s own terms and compiled into this repository\\u0027s AGENTS.md.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022typeference/profiles/spec-conformance@0.1.0\u0022,\n \u0022typeference/profiles/determinism-guardian@0.1.0\u0022,\n \u0022typeference/profiles/trust-signing@0.1.0\u0022,\n \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n ],\n \u0022satisfies\u0022: [],\n \u0022slots\u0022: {\n \u0022repositoryMap\u0022: \u0022typeference/context/repository-map@0.1.0\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Semantic changes land in docs/specification.md before either implementation changes behavior.\u0022,\n \u0022Where the specification and an implementation disagree, the specification wins; record the ruling in docs/decisions and cover it with a conformance fixture.\u0022,\n \u0022Every canonicalization or composition ruling ships with a fixture under conformance/fixtures in the same change.\u0022,\n \u0022Never silently diverge from the specification; fixing the specification is allowed, silent divergence is not.\u0022,\n \u0022Any change to code generation must reproduce the committed digests, verified by the determinism suite before merge.\u0022,\n \u0022Never weaken determinism, provenance, or fail-closed behavior to make a change easier.\u0022,\n \u0022A conformance digest is regenerated only together with the specification change and ADR that justify it; never hand-edit a digest.\u0022,\n \u0022Repeated builds from identical source must stay byte-identical on every platform.\u0022,\n \u0022The signature map always resides outside the source root; moving it inside creates a digest/signature cycle and is forbidden.\u0022,\n \u0022signatureIntent.required fails closed; the unsigned-staging option exists solely to emit payloads for an external signer.\u0022,\n \u0022TypeFerence imports externally produced signatures; it never signs, never verifies cryptographic validity, and never resolves keys.\u0022,\n \u0022Trust metadata is declarative; never dereference identity, attestation, or provenance URIs during compilation.\u0022,\n \u0022Every commit builds and passes the test suite and the determinism suite.\u0022,\n \u0022Decisions with real tradeoffs are recorded as ADRs in docs/decisions before the change merges.\u0022,\n \u0022Documentation must be accurate against the code at the commit that includes it; no fabricated adoption, benchmarks, or endorsements.\u0022,\n \u0022Commit messages are conventional and written for a critical human reader.\u0022\n ],\n \u0022context\u0022: [\n {\n \u0022id\u0022: \u0022typeference/context/contribution-workflow@0.1.0\u0022,\n \u0022contextType\u0022: \u0022typeference/context-types/contribution-workflow@0.1.0\u0022,\n \u0022satisfies\u0022: [\n \u0022typeference/context-types/contribution-workflow@0.1.0\u0022\n ],\n \u0022content\u0022: \u0022# Contribution workflow\\n\\n- Work on feature branches; never rewrite published history; never tag or publish a\\n release outside the checklist in \\u0060docs/release-checklist.md\\u0060.\\n- Before any commit: \\u0060go test ./...\\u0060 (from \\u0060go/\\u0060) and the determinism suite\\n (\\u0060make conformance\\u0060) both pass. A commit that breaks either does not land.\\n- Design decisions with real tradeoffs \\u2014 spec semantics, canonical bytes, trust\\n model, dependencies \\u2014 are recorded in \\u0060docs/decisions/\\u0060 as numbered ADRs in the\\n same change.\\n- Generated artifacts (root \\u0060AGENTS.md\\u0060, \\u0060dist/\\u0060) are only ever changed by\\n regenerating them from source (\\u0060make selfhost\\u0060, \\u0060typeference build\\u0060); hand edits\\n to generated files are drift and CI rejects them.\\n- Documentation is accurate against the code at the commit that includes it. The\\n project describes itself as an experimental reference implementation; no invented\\n adoption, users, benchmarks, or endorsements, ever.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022typeference/context/determinism@0.1.0\u0022,\n \u0022contextType\u0022: \u0022typeference/context-types/determinism@0.1.0\u0022,\n \u0022satisfies\u0022: [\n \u0022typeference/context-types/determinism@0.1.0\u0022\n ],\n \u0022content\u0022: \u0022# Determinism guarantees\\n\\nIdentical source must compile to identical bytes \\u2014 across repeated builds and\\nacross platforms. The guarantee is enforced, not aspirational:\\n\\n- \\u0060conformance/fixtures/\\u0060 records the expected \\u0060typeference-directory-v1\\u0060 digest of\\n every emitted target for the fixture corpus; the compiler must reproduce them in\\n CI (a golden-file determinism suite).\\n- The committed \\u0060dist/\\u0060 tree is the fully materialized reference output for\\n \\u0060examples/helio\\u0060; the compiler byte-compares against it in its tests.\\n- The committed root \\u0060AGENTS.md\\u0060 and \\u0060dist-maintainer/\\u0060 are build artifacts of\\n \\u0060agents/maintainer/\\u0060; CI recompiles the definition and fails on any drift.\\n\\nRules that protect the guarantee:\\n\\n- Digest values are regenerated (\\u0060go test ./conformance -update\\u0060), never typed by\\n hand.\\n- Canonical serialization is defined in \\u0060docs/specification.md\\u0060 (\\u0022Canonicalization\\u0022);\\n any change to it is a specification change with an ADR.\\n- Nothing about determinism, provenance, or fail-closed behavior is ever relaxed to\\n make an unrelated change easier. If a change fights the determinism rules, the\\n change is wrong or the specification needs a recorded amendment.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022typeference/context/repository-map@0.1.0\u0022,\n \u0022contextType\u0022: \u0022typeference/context-types/repository-map@0.1.0\u0022,\n \u0022satisfies\u0022: [\n \u0022typeference/context-types/repository-map@0.1.0\u0022\n ],\n \u0022content\u0022: \u0022# Repository map\\n\\n| Path | Role |\\n| --- | --- |\\n| \\u0060docs/specification.md\\u0060 | Normative specification (source of truth). |\\n| \\u0060docs/whitepaper.md\\u0060 | Motivation and design narrative. |\\n| \\u0060docs/decisions/\\u0060 | Architecture decision records. |\\n| \\u0060go/\\u0060 | The implementation (static binary; module \\u0060github.com/buchk/TypeFerence/go\\u0060). |\\n| \\u0060go/cmd/typeference-lsp/\\u0060 | Language server for \\u0060.tfer\\u0060/\\u0060.yaml\\u0060 authoring. |\\n| \\u0060editors/vscode/\\u0060 | VS Code client for the language server. |\\n| \\u0060go/conformance/\\u0060 | Determinism runner (\\u0060-update\\u0060 regenerates digests). |\\n| \\u0060conformance/\\u0060 | Golden-file fixture corpus. |\\n| \\u0060examples/helio\\u0060 | Example organization used by tests and the quick start. |\\n| \\u0060dist/\\u0060 | Committed reference output of \\u0060examples/helio\\u0060 (byte-compared in CI). |\\n| \\u0060agents/maintainer/\\u0060 | This definition; compiled into the root \\u0060AGENTS.md\\u0060 and \\u0060dist-maintainer/\\u0060. |\\n| \\u0060.github/workflows/ci.yml\\u0060 | Build, test, determinism, and self-host drift gates. |\\n\\nThe root \\u0060AGENTS.md\\u0060 is generated from this definition. To change it, edit the\\nresources under \\u0060agents/maintainer/\\u0060 and run \\u0060make selfhost\\u0060; never edit the\\ngenerated file directly.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022typeference/context/spec-workflow@0.1.0\u0022,\n \u0022contextType\u0022: \u0022typeference/context-types/spec-workflow@0.1.0\u0022,\n \u0022satisfies\u0022: [\n \u0022typeference/context-types/spec-workflow@0.1.0\u0022\n ],\n \u0022content\u0022: \u0022# Specification-first workflow\\n\\nThe normative specification is \\u0060docs/specification.md\\u0060 at the repository root. It is\\nthe source of truth; the Go implementation under \\u0060go/\\u0060 is its reference realization.\\n\\nA semantic change alters valid source, composition, or compiled bytes. It follows:\\n\\n1. Amend \\u0060docs/specification.md\\u0060.\\n2. Record the decision and rejected alternatives in \\u0060docs/decisions/\\u0060.\\n3. Add or update \\u0060conformance/fixtures/\\u0060.\\n4. Update the implementation until determinism passes.\\n\\nIf implementation and specification disagree, the specification wins. Ambiguity is\\na specification bug, not permission for a private implementation rule.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022typeference/context/trust-model@0.1.0\u0022,\n \u0022contextType\u0022: \u0022typeference/context-types/trust-model@0.1.0\u0022,\n \u0022satisfies\u0022: [\n \u0022typeference/context-types/trust-model@0.1.0\u0022\n ],\n \u0022content\u0022: \u0022# Trust model invariants\\n\\n- The signature map lives outside the source root, avoiding a digest/signature cycle.\\n- \\u0060signatureIntent.required\\u0060 fails closed; unsigned staging only emits payloads for\\n an external signer.\\n- TypeFerence imports externally produced signatures; it never signs, verifies\\n cryptographic validity, resolves keys, or dereferences trust URIs.\\n- Identities are ASCII; internationalized authorities use punycode.\\n\\nTrust changes require a specification amendment, ADR, and conformance fixtures.\\n\u0022\n }\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022typeference-maintainer.audit-drift\u0022,\n \u0022capabilityId\u0022: \u0022typeference/capabilities/audit-drift@0.1.0\u0022,\n \u0022implementationId\u0022: \u0022typeference/skills/audit-drift@0.1.0\u0022,\n \u0022description\u0022: \u0022Confirms the committed AGENTS.md and maintainer bundle are exact build artifacts of this definition.\u0022,\n \u0022instructions\u0022: \u0022Run \\u0060typeference diff agents/maintainer --against dist-maintainer --target neutral\\n--emit-ard --publisher-domain typeference.example\\u0060 from the repository root, then\\nbyte-compare the repository-root AGENTS.md against\\ndist-maintainer/neutral/typeference-maintainer/AGENTS.md. Report clean=true only\\nwhen the diff exits 0 and the byte comparison matches. Any drift between the\\ndefinition and its committed artifacts is a broken build: regenerate with\\n\\u0060make selfhost\\u0060 and commit definition and artifacts together, or revert the\\nstray edit to the generated files.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022clean\\u0022:{\\u0022type\\u0022:\\u0022boolean\\u0022},\\u0022driftedFiles\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022clean\\u0022,\\u0022driftedFiles\\u0022]}\u0022,\n \u0022requiresContextTypes\u0022: [\n \u0022typeference/context-types/determinism@0.1.0\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022typeference/capabilities/audit-drift@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022typeference/skills/audit-drift@0.1.0\u0022\n }\n ]\n },\n {\n \u0022dispatchName\u0022: \u0022typeference-maintainer.verify-conformance\u0022,\n \u0022capabilityId\u0022: \u0022typeference/capabilities/verify-conformance@0.1.0\u0022,\n \u0022implementationId\u0022: \u0022typeference/skills/verify-conformance@0.1.0\u0022,\n \u0022description\u0022: \u0022Runs the determinism suite and reports whether the compiler reproduces the committed digests.\u0022,\n \u0022instructions\u0022: \u0022Run \\u0060go test ./conformance\\u0060 from the go/ directory (or \\u0060make conformance\\u0060).\\nReport passed=true only when every fixture reproduces its committed digest.\\nList each failing fixture and target as a mismatch. Never resolve a mismatch\\nby editing a digest; find the compiler regression, or take the ruling to the\\nspecification with an ADR and a regenerated fixture.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022passed\\u0022:{\\u0022type\\u0022:\\u0022boolean\\u0022},\\u0022mismatches\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022passed\\u0022,\\u0022mismatches\\u0022]}\u0022,\n \u0022requiresContextTypes\u0022: [\n \u0022typeference/context-types/determinism@0.1.0\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022typeference/capabilities/verify-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022typeference/skills/verify-conformance@0.1.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/spec-conformance@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/determinism-guardian@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/trust-signing@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/contribution-workflow@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repositoryMap\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n }\n ]\n}\n"
},
{
"path": "provenance.json",
"mediaType": "application/json",
- "content": "[\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/spec-conformance@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/determinism-guardian@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/trust-signing@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/contribution-workflow@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repositoryMap\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n }\n]\n"
+ "content": "[\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/spec-conformance@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/determinism-guardian@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/trust-signing@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022typeference/profiles/contribution-workflow@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/spec-conformance@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/determinism-guardian@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/trust-signing@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.typeference/profiles/contribution-workflow@0.1.0\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repositoryMap\u0022,\n \u0022source\u0022: \u0022typeference/typeference-maintainer@0.1.0\u0022\n }\n]\n"
},
{
"path": "skills/audit-drift/SKILL.md",
"mediaType": "text/markdown",
- "content": "---\nname: audit-drift\ndescription: \u0022Confirms the committed AGENTS.md and maintainer bundle are exact build artifacts of this definition.\u0022\n---\n\nRun \u0060typeference diff agents/maintainer --against dist-maintainer --target neutral\n--emit-ard --publisher-domain typeference.example\u0060 from the repository root, then\nbyte-compare the repository-root AGENTS.md against\ndist-maintainer/neutral/typeference-maintainer/AGENTS.md. Report clean=true only\nwhen the diff exits 0 and the byte comparison matches. Any drift between the\ndefinition and its committed artifacts is a broken build: regenerate with\n\u0060make selfhost\u0060 and commit definition and artifacts together, or revert the\nstray edit to the generated files.\n\n## Context loaded on invocation\n\n- \u0060context/determinism.md\u0060\n"
+ "content": "---\nname: audit-drift\ndescription: \u0022Confirms the committed AGENTS.md and maintainer bundle are exact build artifacts of this definition.\u0022\n---\n\nRun \u0060typeference diff agents/maintainer --against dist-maintainer --target neutral\n--emit-ard --publisher-domain typeference.example\u0060 from the repository root, then\nbyte-compare the repository-root AGENTS.md against\ndist-maintainer/neutral/typeference-maintainer/AGENTS.md. Report clean=true only\nwhen the diff exits 0 and the byte comparison matches. Any drift between the\ndefinition and its committed artifacts is a broken build: regenerate with\n\u0060make selfhost\u0060 and commit definition and artifacts together, or revert the\nstray edit to the generated files.\n"
},
{
"path": "skills/verify-conformance/SKILL.md",
"mediaType": "text/markdown",
- "content": "---\nname: verify-conformance\ndescription: \u0022Runs the shared conformance suite on both implementations and reports any digest disagreement.\u0022\n---\n\nRun \u0060go test ./conformance\u0060 from the go/ directory, then run\n\u0060dotnet test TypeFerence.slnx --filter FullyQualifiedName~ConformanceSuiteTests\u0060\nfrom the repository root (or \u0060make conformance\u0060 for both). Report passed=true\nonly when every fixture passes on both implementations. List each failing\nfixture and target as a mismatch. Never resolve a mismatch by editing a\ndigest; find the diverging implementation or take the ruling to the\nspecification with an ADR and a new fixture.\n\n## Context loaded on invocation\n\n- \u0060context/determinism.md\u0060\n"
+ "content": "---\nname: verify-conformance\ndescription: \u0022Runs the determinism suite and reports whether the compiler reproduces the committed digests.\u0022\n---\n\nRun \u0060go test ./conformance\u0060 from the go/ directory (or \u0060make conformance\u0060).\nReport passed=true only when every fixture reproduces its committed digest.\nList each failing fixture and target as a mismatch. Never resolve a mismatch\nby editing a digest; find the compiler regression, or take the ruling to the\nspecification with an ADR and a regenerated fixture.\n"
}
]
},
"metadata": {
"generatedBy": "TypeFerence",
- "sourceDigest": "sha256:c42776752aef743bd24749eca2f06b0da9b27d51b9edfce13984e39e0aa565c2",
- "sourceIdentifier": "urn:air:typeference.example:typeference:source:maintainer",
+ "sourceDigest": "sha256:5c65966e441ae60e7e4eadda8e4794f87ec9dbca736a922f32373b06b5ebc4b2",
+ "sourceIdentifier": "urn:air:typeference.example:typeference:source:typeference-maintainer",
"target": "neutral"
},
"trustManifest": {
@@ -146,8 +181,8 @@
"provenance": [
{
"relation": "derivedFrom",
- "sourceId": "urn:air:typeference.example:typeference:source:maintainer",
- "sourceDigest": "sha256:c42776752aef743bd24749eca2f06b0da9b27d51b9edfce13984e39e0aa565c2"
+ "sourceId": "urn:air:typeference.example:typeference:source:typeference-maintainer",
+ "sourceDigest": "sha256:5c65966e441ae60e7e4eadda8e4794f87ec9dbca736a922f32373b06b5ebc4b2"
}
]
}
diff --git a/dist-maintainer/neutral/.typeference/build.json b/dist-maintainer/neutral/.typeference/build.json
new file mode 100644
index 0000000..e5ed123
--- /dev/null
+++ b/dist-maintainer/neutral/.typeference/build.json
@@ -0,0 +1,12 @@
+{
+ "schemaVersion": 1,
+ "target": "neutral",
+ "sourceDigest": "sha256:5c65966e441ae60e7e4eadda8e4794f87ec9dbca736a922f32373b06b5ebc4b2",
+ "artifacts": [
+ {
+ "agentId": "typeference/typeference-maintainer@0.1.0",
+ "path": "typeference-maintainer",
+ "digest": "sha256:6815314acf678d951ba331162f63c3e758ae4d2bae6cff1a52cc22a9bc5794a0"
+ }
+ ]
+}
diff --git a/dist-maintainer/neutral/typeference-maintainer/.typeference/link.json b/dist-maintainer/neutral/typeference-maintainer/.typeference/link.json
new file mode 100644
index 0000000..00dc308
--- /dev/null
+++ b/dist-maintainer/neutral/typeference-maintainer/.typeference/link.json
@@ -0,0 +1,10 @@
+{
+ "schemaVersion": 1,
+ "agentId": "typeference/typeference-maintainer@0.1.0",
+ "target": "neutral",
+ "defaultMode": "",
+ "sourceDigest": "sha256:5c65966e441ae60e7e4eadda8e4794f87ec9dbca736a922f32373b06b5ebc4b2",
+ "dependencies": [],
+ "modes": [],
+ "toolImports": []
+}
diff --git a/dist-maintainer/neutral/typeference-maintainer/AGENTS.md b/dist-maintainer/neutral/typeference-maintainer/AGENTS.md
index 44fb18d..88c5e40 100644
--- a/dist-maintainer/neutral/typeference-maintainer/AGENTS.md
+++ b/dist-maintainer/neutral/typeference-maintainer/AGENTS.md
@@ -8,7 +8,7 @@ The agent that maintains the TypeFerence repository, defined in TypeFerence's ow
- Where the specification and an implementation disagree, the specification wins; record the ruling in docs/decisions and cover it with a conformance fixture.
- Every canonicalization or composition ruling ships with a fixture under conformance/fixtures in the same change.
- Never silently diverge from the specification; fixing the specification is allowed, silent divergence is not.
-- Any change to code generation must keep the C# and Go implementations byte-identical, verified by the conformance suite before merge.
+- Any change to code generation must reproduce the committed digests, verified by the determinism suite before merge.
- Never weaken determinism, provenance, or fail-closed behavior to make a change easier.
- A conformance digest is regenerated only together with the specification change and ADR that justify it; never hand-edit a digest.
- Repeated builds from identical source must stay byte-identical on every platform.
@@ -16,17 +16,115 @@ The agent that maintains the TypeFerence repository, defined in TypeFerence's ow
- signatureIntent.required fails closed; the unsigned-staging option exists solely to emit payloads for an external signer.
- TypeFerence imports externally produced signatures; it never signs, never verifies cryptographic validity, and never resolves keys.
- Trust metadata is declarative; never dereference identity, attestation, or provenance URIs during compilation.
-- Every commit builds and passes both implementations' test suites and the shared conformance suite.
+- Every commit builds and passes the test suite and the determinism suite.
- Decisions with real tradeoffs are recorded as ADRs in docs/decisions before the change merges.
- Documentation must be accurate against the code at the commit that includes it; no fabricated adoption, benchmarks, or endorsements.
- Commit messages are conventional and written for a critical human reader.
## Context slots
-- `repositoryMap`: `context/repository-map.md`
+- `repositoryMap`: `typeference/context/repository-map@0.1.0`
+
+## Context
+
+### Contribution Workflow
+
+# Contribution workflow
+
+- Work on feature branches; never rewrite published history; never tag or publish a
+ release outside the checklist in `docs/release-checklist.md`.
+- Before any commit: `go test ./...` (from `go/`) and the determinism suite
+ (`make conformance`) both pass. A commit that breaks either does not land.
+- Design decisions with real tradeoffs — spec semantics, canonical bytes, trust
+ model, dependencies — are recorded in `docs/decisions/` as numbered ADRs in the
+ same change.
+- Generated artifacts (root `AGENTS.md`, `dist/`) are only ever changed by
+ regenerating them from source (`make selfhost`, `typeference build`); hand edits
+ to generated files are drift and CI rejects them.
+- Documentation is accurate against the code at the commit that includes it. The
+ project describes itself as an experimental reference implementation; no invented
+ adoption, users, benchmarks, or endorsements, ever.
+
+### Determinism Guarantees
+
+# Determinism guarantees
+
+Identical source must compile to identical bytes — across repeated builds and
+across platforms. The guarantee is enforced, not aspirational:
+
+- `conformance/fixtures/` records the expected `typeference-directory-v1` digest of
+ every emitted target for the fixture corpus; the compiler must reproduce them in
+ CI (a golden-file determinism suite).
+- The committed `dist/` tree is the fully materialized reference output for
+ `examples/helio`; the compiler byte-compares against it in its tests.
+- The committed root `AGENTS.md` and `dist-maintainer/` are build artifacts of
+ `agents/maintainer/`; CI recompiles the definition and fails on any drift.
+
+Rules that protect the guarantee:
+
+- Digest values are regenerated (`go test ./conformance -update`), never typed by
+ hand.
+- Canonical serialization is defined in `docs/specification.md` ("Canonicalization");
+ any change to it is a specification change with an ADR.
+- Nothing about determinism, provenance, or fail-closed behavior is ever relaxed to
+ make an unrelated change easier. If a change fights the determinism rules, the
+ change is wrong or the specification needs a recorded amendment.
+
+### Repository Map
+
+# Repository map
+
+| Path | Role |
+| --- | --- |
+| `docs/specification.md` | Normative specification (source of truth). |
+| `docs/whitepaper.md` | Motivation and design narrative. |
+| `docs/decisions/` | Architecture decision records. |
+| `go/` | The implementation (static binary; module `github.com/buchk/TypeFerence/go`). |
+| `go/cmd/typeference-lsp/` | Language server for `.tfer`/`.yaml` authoring. |
+| `editors/vscode/` | VS Code client for the language server. |
+| `go/conformance/` | Determinism runner (`-update` regenerates digests). |
+| `conformance/` | Golden-file fixture corpus. |
+| `examples/helio` | Example organization used by tests and the quick start. |
+| `dist/` | Committed reference output of `examples/helio` (byte-compared in CI). |
+| `agents/maintainer/` | This definition; compiled into the root `AGENTS.md` and `dist-maintainer/`. |
+| `.github/workflows/ci.yml` | Build, test, determinism, and self-host drift gates. |
+
+The root `AGENTS.md` is generated from this definition. To change it, edit the
+resources under `agents/maintainer/` and run `make selfhost`; never edit the
+generated file directly.
+
+### Specification-first Workflow
+
+# Specification-first workflow
+
+The normative specification is `docs/specification.md` at the repository root. It is
+the source of truth; the Go implementation under `go/` is its reference realization.
+
+A semantic change alters valid source, composition, or compiled bytes. It follows:
+
+1. Amend `docs/specification.md`.
+2. Record the decision and rejected alternatives in `docs/decisions/`.
+3. Add or update `conformance/fixtures/`.
+4. Update the implementation until determinism passes.
+
+If implementation and specification disagree, the specification wins. Ambiguity is
+a specification bug, not permission for a private implementation rule.
+
+### Trust Model
+
+# Trust model invariants
+
+- The signature map lives outside the source root, avoiding a digest/signature cycle.
+- `signatureIntent.required` fails closed; unsigned staging only emits payloads for
+ an external signer.
+- TypeFerence imports externally produced signatures; it never signs, verifies
+ cryptographic validity, resolves keys, or dereferences trust URIs.
+- Identities are ASCII; internationalized authorities use punycode.
+
+Trust changes require a specification amendment, ADR, and conformance fixtures.
## Available skills
- `typeference-maintainer.audit-drift`: Confirms the committed AGENTS.md and maintainer bundle are exact build artifacts of this definition.
-- `typeference-maintainer.verify-conformance`: Runs the shared conformance suite on both implementations and reports any digest disagreement.
+- `typeference-maintainer.verify-conformance`: Runs the determinism suite and reports whether the compiler reproduces the committed digests.
diff --git a/dist-maintainer/neutral/typeference-maintainer/bundle.json b/dist-maintainer/neutral/typeference-maintainer/bundle.json
index eb08bf3..31befae 100644
--- a/dist-maintainer/neutral/typeference-maintainer/bundle.json
+++ b/dist-maintainer/neutral/typeference-maintainer/bundle.json
@@ -11,14 +11,14 @@
],
"satisfies": [],
"slots": {
- "repositoryMap": "context/repository-map.md"
+ "repositoryMap": "typeference/context/repository-map@0.1.0"
},
"workingNorms": [
"Semantic changes land in docs/specification.md before either implementation changes behavior.",
"Where the specification and an implementation disagree, the specification wins; record the ruling in docs/decisions and cover it with a conformance fixture.",
"Every canonicalization or composition ruling ships with a fixture under conformance/fixtures in the same change.",
"Never silently diverge from the specification; fixing the specification is allowed, silent divergence is not.",
- "Any change to code generation must keep the C# and Go implementations byte-identical, verified by the conformance suite before merge.",
+ "Any change to code generation must reproduce the committed digests, verified by the determinism suite before merge.",
"Never weaken determinism, provenance, or fail-closed behavior to make a change easier.",
"A conformance digest is regenerated only together with the specification change and ADR that justify it; never hand-edit a digest.",
"Repeated builds from identical source must stay byte-identical on every platform.",
@@ -26,17 +26,52 @@
"signatureIntent.required fails closed; the unsigned-staging option exists solely to emit payloads for an external signer.",
"TypeFerence imports externally produced signatures; it never signs, never verifies cryptographic validity, and never resolves keys.",
"Trust metadata is declarative; never dereference identity, attestation, or provenance URIs during compilation.",
- "Every commit builds and passes both implementations\u0027 test suites and the shared conformance suite.",
+ "Every commit builds and passes the test suite and the determinism suite.",
"Decisions with real tradeoffs are recorded as ADRs in docs/decisions before the change merges.",
"Documentation must be accurate against the code at the commit that includes it; no fabricated adoption, benchmarks, or endorsements.",
"Commit messages are conventional and written for a critical human reader."
],
- "contextFiles": [
- "context/spec-workflow.md",
- "context/determinism.md",
- "context/trust-model.md",
- "context/contribution.md",
- "context/repository-map.md"
+ "context": [
+ {
+ "id": "typeference/context/contribution-workflow@0.1.0",
+ "contextType": "typeference/context-types/contribution-workflow@0.1.0",
+ "satisfies": [
+ "typeference/context-types/contribution-workflow@0.1.0"
+ ],
+ "content": "# Contribution workflow\n\n- Work on feature branches; never rewrite published history; never tag or publish a\n release outside the checklist in \u0060docs/release-checklist.md\u0060.\n- Before any commit: \u0060go test ./...\u0060 (from \u0060go/\u0060) and the determinism suite\n (\u0060make conformance\u0060) both pass. A commit that breaks either does not land.\n- Design decisions with real tradeoffs \u2014 spec semantics, canonical bytes, trust\n model, dependencies \u2014 are recorded in \u0060docs/decisions/\u0060 as numbered ADRs in the\n same change.\n- Generated artifacts (root \u0060AGENTS.md\u0060, \u0060dist/\u0060) are only ever changed by\n regenerating them from source (\u0060make selfhost\u0060, \u0060typeference build\u0060); hand edits\n to generated files are drift and CI rejects them.\n- Documentation is accurate against the code at the commit that includes it. The\n project describes itself as an experimental reference implementation; no invented\n adoption, users, benchmarks, or endorsements, ever.\n"
+ },
+ {
+ "id": "typeference/context/determinism@0.1.0",
+ "contextType": "typeference/context-types/determinism@0.1.0",
+ "satisfies": [
+ "typeference/context-types/determinism@0.1.0"
+ ],
+ "content": "# Determinism guarantees\n\nIdentical source must compile to identical bytes \u2014 across repeated builds and\nacross platforms. The guarantee is enforced, not aspirational:\n\n- \u0060conformance/fixtures/\u0060 records the expected \u0060typeference-directory-v1\u0060 digest of\n every emitted target for the fixture corpus; the compiler must reproduce them in\n CI (a golden-file determinism suite).\n- The committed \u0060dist/\u0060 tree is the fully materialized reference output for\n \u0060examples/helio\u0060; the compiler byte-compares against it in its tests.\n- The committed root \u0060AGENTS.md\u0060 and \u0060dist-maintainer/\u0060 are build artifacts of\n \u0060agents/maintainer/\u0060; CI recompiles the definition and fails on any drift.\n\nRules that protect the guarantee:\n\n- Digest values are regenerated (\u0060go test ./conformance -update\u0060), never typed by\n hand.\n- Canonical serialization is defined in \u0060docs/specification.md\u0060 (\u0022Canonicalization\u0022);\n any change to it is a specification change with an ADR.\n- Nothing about determinism, provenance, or fail-closed behavior is ever relaxed to\n make an unrelated change easier. If a change fights the determinism rules, the\n change is wrong or the specification needs a recorded amendment.\n"
+ },
+ {
+ "id": "typeference/context/repository-map@0.1.0",
+ "contextType": "typeference/context-types/repository-map@0.1.0",
+ "satisfies": [
+ "typeference/context-types/repository-map@0.1.0"
+ ],
+ "content": "# Repository map\n\n| Path | Role |\n| --- | --- |\n| \u0060docs/specification.md\u0060 | Normative specification (source of truth). |\n| \u0060docs/whitepaper.md\u0060 | Motivation and design narrative. |\n| \u0060docs/decisions/\u0060 | Architecture decision records. |\n| \u0060go/\u0060 | The implementation (static binary; module \u0060github.com/buchk/TypeFerence/go\u0060). |\n| \u0060go/cmd/typeference-lsp/\u0060 | Language server for \u0060.tfer\u0060/\u0060.yaml\u0060 authoring. |\n| \u0060editors/vscode/\u0060 | VS Code client for the language server. |\n| \u0060go/conformance/\u0060 | Determinism runner (\u0060-update\u0060 regenerates digests). |\n| \u0060conformance/\u0060 | Golden-file fixture corpus. |\n| \u0060examples/helio\u0060 | Example organization used by tests and the quick start. |\n| \u0060dist/\u0060 | Committed reference output of \u0060examples/helio\u0060 (byte-compared in CI). |\n| \u0060agents/maintainer/\u0060 | This definition; compiled into the root \u0060AGENTS.md\u0060 and \u0060dist-maintainer/\u0060. |\n| \u0060.github/workflows/ci.yml\u0060 | Build, test, determinism, and self-host drift gates. |\n\nThe root \u0060AGENTS.md\u0060 is generated from this definition. To change it, edit the\nresources under \u0060agents/maintainer/\u0060 and run \u0060make selfhost\u0060; never edit the\ngenerated file directly.\n"
+ },
+ {
+ "id": "typeference/context/spec-workflow@0.1.0",
+ "contextType": "typeference/context-types/spec-workflow@0.1.0",
+ "satisfies": [
+ "typeference/context-types/spec-workflow@0.1.0"
+ ],
+ "content": "# Specification-first workflow\n\nThe normative specification is \u0060docs/specification.md\u0060 at the repository root. It is\nthe source of truth; the Go implementation under \u0060go/\u0060 is its reference realization.\n\nA semantic change alters valid source, composition, or compiled bytes. It follows:\n\n1. Amend \u0060docs/specification.md\u0060.\n2. Record the decision and rejected alternatives in \u0060docs/decisions/\u0060.\n3. Add or update \u0060conformance/fixtures/\u0060.\n4. Update the implementation until determinism passes.\n\nIf implementation and specification disagree, the specification wins. Ambiguity is\na specification bug, not permission for a private implementation rule.\n"
+ },
+ {
+ "id": "typeference/context/trust-model@0.1.0",
+ "contextType": "typeference/context-types/trust-model@0.1.0",
+ "satisfies": [
+ "typeference/context-types/trust-model@0.1.0"
+ ],
+ "content": "# Trust model invariants\n\n- The signature map lives outside the source root, avoiding a digest/signature cycle.\n- \u0060signatureIntent.required\u0060 fails closed; unsigned staging only emits payloads for\n an external signer.\n- TypeFerence imports externally produced signatures; it never signs, verifies\n cryptographic validity, resolves keys, or dereferences trust URIs.\n- Identities are ASCII; internationalized authorities use punycode.\n\nTrust changes require a specification amendment, ADR, and conformance fixtures.\n"
+ }
],
"skills": [
{
@@ -47,8 +82,8 @@
"instructions": "Run \u0060typeference diff agents/maintainer --against dist-maintainer --target neutral\n--emit-ard --publisher-domain typeference.example\u0060 from the repository root, then\nbyte-compare the repository-root AGENTS.md against\ndist-maintainer/neutral/typeference-maintainer/AGENTS.md. Report clean=true only\nwhen the diff exits 0 and the byte comparison matches. Any drift between the\ndefinition and its committed artifacts is a broken build: regenerate with\n\u0060make selfhost\u0060 and commit definition and artifacts together, or revert the\nstray edit to the generated files.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022clean\u0022:{\u0022type\u0022:\u0022boolean\u0022},\u0022driftedFiles\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022clean\u0022,\u0022driftedFiles\u0022]}",
- "contextFiles": [
- "context/determinism.md"
+ "requiresContextTypes": [
+ "typeference/context-types/determinism@0.1.0"
],
"provenance": [
{
@@ -65,12 +100,12 @@
"dispatchName": "typeference-maintainer.verify-conformance",
"capabilityId": "typeference/capabilities/verify-conformance@0.1.0",
"implementationId": "typeference/skills/verify-conformance@0.1.0",
- "description": "Runs the shared conformance suite on both implementations and reports any digest disagreement.",
- "instructions": "Run \u0060go test ./conformance\u0060 from the go/ directory, then run\n\u0060dotnet test TypeFerence.slnx --filter FullyQualifiedName~ConformanceSuiteTests\u0060\nfrom the repository root (or \u0060make conformance\u0060 for both). Report passed=true\nonly when every fixture passes on both implementations. List each failing\nfixture and target as a mismatch. Never resolve a mismatch by editing a\ndigest; find the diverging implementation or take the ruling to the\nspecification with an ADR and a new fixture.\n",
+ "description": "Runs the determinism suite and reports whether the compiler reproduces the committed digests.",
+ "instructions": "Run \u0060go test ./conformance\u0060 from the go/ directory (or \u0060make conformance\u0060).\nReport passed=true only when every fixture reproduces its committed digest.\nList each failing fixture and target as a mismatch. Never resolve a mismatch\nby editing a digest; find the compiler regression, or take the ruling to the\nspecification with an ADR and a regenerated fixture.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022passed\u0022:{\u0022type\u0022:\u0022boolean\u0022},\u0022mismatches\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022passed\u0022,\u0022mismatches\u0022]}",
- "contextFiles": [
- "context/determinism.md"
+ "requiresContextTypes": [
+ "typeference/context-types/determinism@0.1.0"
],
"provenance": [
{
@@ -101,10 +136,6 @@
"field": "workingNorms",
"source": "typeference/profiles/spec-conformance@0.1.0"
},
- {
- "field": "contextFiles",
- "source": "typeference/profiles/spec-conformance@0.1.0"
- },
{
"field": "workingNorms",
"source": "typeference/profiles/determinism-guardian@0.1.0"
@@ -121,10 +152,6 @@
"field": "workingNorms",
"source": "typeference/profiles/determinism-guardian@0.1.0"
},
- {
- "field": "contextFiles",
- "source": "typeference/profiles/determinism-guardian@0.1.0"
- },
{
"field": "workingNorms",
"source": "typeference/profiles/trust-signing@0.1.0"
@@ -141,10 +168,6 @@
"field": "workingNorms",
"source": "typeference/profiles/trust-signing@0.1.0"
},
- {
- "field": "contextFiles",
- "source": "typeference/profiles/trust-signing@0.1.0"
- },
{
"field": "workingNorms",
"source": "typeference/profiles/contribution-workflow@0.1.0"
@@ -161,10 +184,6 @@
"field": "workingNorms",
"source": "typeference/profiles/contribution-workflow@0.1.0"
},
- {
- "field": "contextFiles",
- "source": "typeference/profiles/contribution-workflow@0.1.0"
- },
{
"field": "embeds.typeference/profiles/spec-conformance@0.1.0",
"source": "typeference/typeference-maintainer@0.1.0"
@@ -192,10 +211,6 @@
{
"field": "slots.repositoryMap",
"source": "typeference/typeference-maintainer@0.1.0"
- },
- {
- "field": "contextFiles",
- "source": "typeference/typeference-maintainer@0.1.0"
}
]
}
diff --git a/dist-maintainer/neutral/typeference-maintainer/provenance.json b/dist-maintainer/neutral/typeference-maintainer/provenance.json
index ee7d61a..40586a3 100644
--- a/dist-maintainer/neutral/typeference-maintainer/provenance.json
+++ b/dist-maintainer/neutral/typeference-maintainer/provenance.json
@@ -15,10 +15,6 @@
"field": "workingNorms",
"source": "typeference/profiles/spec-conformance@0.1.0"
},
- {
- "field": "contextFiles",
- "source": "typeference/profiles/spec-conformance@0.1.0"
- },
{
"field": "workingNorms",
"source": "typeference/profiles/determinism-guardian@0.1.0"
@@ -35,10 +31,6 @@
"field": "workingNorms",
"source": "typeference/profiles/determinism-guardian@0.1.0"
},
- {
- "field": "contextFiles",
- "source": "typeference/profiles/determinism-guardian@0.1.0"
- },
{
"field": "workingNorms",
"source": "typeference/profiles/trust-signing@0.1.0"
@@ -55,10 +47,6 @@
"field": "workingNorms",
"source": "typeference/profiles/trust-signing@0.1.0"
},
- {
- "field": "contextFiles",
- "source": "typeference/profiles/trust-signing@0.1.0"
- },
{
"field": "workingNorms",
"source": "typeference/profiles/contribution-workflow@0.1.0"
@@ -75,10 +63,6 @@
"field": "workingNorms",
"source": "typeference/profiles/contribution-workflow@0.1.0"
},
- {
- "field": "contextFiles",
- "source": "typeference/profiles/contribution-workflow@0.1.0"
- },
{
"field": "embeds.typeference/profiles/spec-conformance@0.1.0",
"source": "typeference/typeference-maintainer@0.1.0"
@@ -106,9 +90,5 @@
{
"field": "slots.repositoryMap",
"source": "typeference/typeference-maintainer@0.1.0"
- },
- {
- "field": "contextFiles",
- "source": "typeference/typeference-maintainer@0.1.0"
}
]
diff --git a/dist-maintainer/neutral/typeference-maintainer/skills/audit-drift/SKILL.md b/dist-maintainer/neutral/typeference-maintainer/skills/audit-drift/SKILL.md
index fadb1a1..415cfd1 100644
--- a/dist-maintainer/neutral/typeference-maintainer/skills/audit-drift/SKILL.md
+++ b/dist-maintainer/neutral/typeference-maintainer/skills/audit-drift/SKILL.md
@@ -11,7 +11,3 @@ when the diff exits 0 and the byte comparison matches. Any drift between the
definition and its committed artifacts is a broken build: regenerate with
`make selfhost` and commit definition and artifacts together, or revert the
stray edit to the generated files.
-
-## Context loaded on invocation
-
-- `context/determinism.md`
diff --git a/dist-maintainer/neutral/typeference-maintainer/skills/verify-conformance/SKILL.md b/dist-maintainer/neutral/typeference-maintainer/skills/verify-conformance/SKILL.md
index 4ee255d..bd690ae 100644
--- a/dist-maintainer/neutral/typeference-maintainer/skills/verify-conformance/SKILL.md
+++ b/dist-maintainer/neutral/typeference-maintainer/skills/verify-conformance/SKILL.md
@@ -1,16 +1,10 @@
---
name: verify-conformance
-description: "Runs the shared conformance suite on both implementations and reports any digest disagreement."
+description: "Runs the determinism suite and reports whether the compiler reproduces the committed digests."
---
-Run `go test ./conformance` from the go/ directory, then run
-`dotnet test TypeFerence.slnx --filter FullyQualifiedName~ConformanceSuiteTests`
-from the repository root (or `make conformance` for both). Report passed=true
-only when every fixture passes on both implementations. List each failing
-fixture and target as a mismatch. Never resolve a mismatch by editing a
-digest; find the diverging implementation or take the ruling to the
-specification with an ADR and a new fixture.
-
-## Context loaded on invocation
-
-- `context/determinism.md`
+Run `go test ./conformance` from the go/ directory (or `make conformance`).
+Report passed=true only when every fixture reproduces its committed digest.
+List each failing fixture and target as a mismatch. Never resolve a mismatch
+by editing a digest; find the compiler regression, or take the ruling to the
+specification with an ADR and a regenerated fixture.
diff --git a/dist/ard/ai-catalog.json b/dist/ard/ai-catalog.json
index c2812d9..518562e 100644
--- a/dist/ard/ai-catalog.json
+++ b/dist/ard/ai-catalog.json
@@ -6,114 +6,159 @@
},
"entries": [
{
- "identifier": "urn:air:helio.example:typeference:source:helio",
- "displayName": "TypeFerence source package: helio",
+ "identifier": "urn:air:helio.example:typeference:source:helio-agents",
+ "displayName": "TypeFerence source package: helio-agents",
"type": "application/vnd.typeference.source-package\u002Bjson",
"description": "Canonical typed source package for validation, audit, and reproducible compilation.",
"version": "1.0.0",
"data": {
"schemaVersion": 1,
- "digest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26",
+ "digest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
"files": [
{
- "path": "agents/executive-assistant.agent.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: agent\nid: helio/executive-assistant@1.0.0\ndisplayName: Helio Executive Assistant\ndescription: Coordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\nembeds:\n - helio/profiles/person-defaults@1.0.0\ncontextFiles:\n - context/executive-rhythm.md\nskills:\n - ref: helio/skills/prepare-brief@1.0.0\n"
+ "path": "agents/executive-assistant.agent.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: agent\nid: helio/executive-assistant@1.0.0\ndisplayName: Helio Executive Assistant\ndescription: Coordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\nembeds:\n - helio/profiles/person-defaults@1.0.0\ncontext:\n - helio/context/executive-rhythm@1.0.0\nskills:\n - ref: helio/skills/prepare-brief@1.0.0\n---\n"
},
{
- "path": "agents/payments-repo-agent.agent.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: agent\nid: helio/payments-repo-agent@1.0.0\ndisplayName: Helio Payments Repository Agent\ndescription: Specializes repository assistance for the fictional payments service.\nembeds:\n - helio/profiles/repository-defaults@1.0.0\ncontextFiles:\n - context/payments-service.md\nskills:\n - ref: helio/skills/payments-repository-status@1.0.0\n capability: helio/capabilities/repository-status@1.0.0\n"
+ "path": "agents/payments-repo-agent.agent.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: agent\nid: helio/payments-repo-agent@1.0.0\ndisplayName: Helio Payments Repository Agent\ndescription: Specializes repository assistance for the fictional payments service.\nembeds:\n - helio/profiles/repository-defaults@1.0.0\ncontext:\n - helio/context/payments-service@1.0.0\nskills:\n - ref: helio/skills/payments-repository-status@1.0.0\n capability: helio/capabilities/repository-status@1.0.0\n---\n"
},
{
- "path": "capabilities/prepare-brief.capability.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: capability\nid: helio/capabilities/prepare-brief@1.0.0\ndisplayName: Prepare Brief\ndescription: Capability slot for assembling decision-oriented executive briefs.\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022topic\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022topic\u0022],\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022brief\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022openQuestions\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022brief\u0022,\u0022openQuestions\u0022]}\u0027\n"
+ "path": "capabilities/prepare-brief.capability.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: capability\nid: helio/capabilities/prepare-brief@1.0.0\ndisplayName: Prepare Brief\ndescription: Capability slot for assembling decision-oriented executive briefs.\nvisibility: exposed\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022topic\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022topic\u0022],\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022brief\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022openQuestions\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022brief\u0022,\u0022openQuestions\u0022]}\u0027\n---\n"
},
{
- "path": "capabilities/repository-status.capability.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: capability\nid: helio/capabilities/repository-status@1.0.0\ndisplayName: Repository Status\ndescription: Capability slot for evidence-backed repository health summaries.\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022focus\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022status\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022risks\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022status\u0022,\u0022evidence\u0022,\u0022risks\u0022]}\u0027\n"
+ "path": "capabilities/repository-status.capability.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: capability\nid: helio/capabilities/repository-status@1.0.0\ndisplayName: Repository Status\ndescription: Capability slot for evidence-backed repository health summaries.\nvisibility: exposed\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022focus\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022status\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022risks\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022status\u0022,\u0022evidence\u0022,\u0022risks\u0022]}\u0027\n---\n"
},
{
- "path": "capabilities/triage-message.capability.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: capability\nid: helio/capabilities/triage-message@1.0.0\ndisplayName: Triage Message\ndescription: Capability slot for classifying inbound messages and selecting accountable next actions.\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022message\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022message\u0022],\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022summary\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022urgency\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022owner\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022nextAction\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022summary\u0022,\u0022urgency\u0022,\u0022owner\u0022,\u0022nextAction\u0022]}\u0027\n"
+ "path": "capabilities/triage-message.capability.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: capability\nid: helio/capabilities/triage-message@1.0.0\ndisplayName: Triage Message\ndescription: Capability slot for classifying inbound messages and selecting accountable next actions.\nvisibility: exposed\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022message\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022message\u0022],\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022summary\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022urgency\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022owner\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022nextAction\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022summary\u0022,\u0022urgency\u0022,\u0022owner\u0022,\u0022nextAction\u0022]}\u0027\n---\n"
},
{
- "path": "context/executive-rhythm.md",
- "mediaType": "text/markdown",
- "content": "# Executive rhythm\n\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\n"
+ "path": "context-types/executive-rhythm.contexttype.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: contextType\nid: helio/context-types/executive-rhythm@1.0.0\ndisplayName: Executive Rhythm\nbody:\n type: text\n required: true\n---\n"
},
{
- "path": "context/organization.md",
- "mediaType": "text/markdown",
- "content": "# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n"
+ "path": "context-types/organization.contexttype.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: contextType\nid: helio/context-types/organization@1.0.0\ndisplayName: Organization\nbody:\n type: text\n required: true\n---\n"
},
{
- "path": "context/payments-service.md",
- "mediaType": "text/markdown",
- "content": "# Payments service\n\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\n"
+ "path": "context-types/payments-service.contexttype.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: contextType\nid: helio/context-types/payments-service@1.0.0\ndisplayName: Payments Service\nbody:\n type: text\n required: true\n---\n"
},
{
- "path": "context/principal.md",
- "mediaType": "text/markdown",
- "content": "# Principal\n\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\n"
+ "path": "context-types/principal.contexttype.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: contextType\nid: helio/context-types/principal@1.0.0\ndisplayName: Principal Preferences\nbody:\n type: text\n required: true\n---\n"
},
{
- "path": "context/repository.md",
- "mediaType": "text/markdown",
- "content": "# Repository evidence\n\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\n"
+ "path": "context-types/repository-evidence.contexttype.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: contextType\nid: helio/context-types/repository-evidence@1.0.0\ndisplayName: Repository Evidence\nbody:\n type: text\n required: true\n---\n"
},
{
- "path": "context/safety-policy.md",
- "mediaType": "text/markdown",
- "content": "# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n"
+ "path": "context-types/safety-policy.contexttype.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: contextType\nid: helio/context-types/safety-policy@1.0.0\ndisplayName: Safety Policy\ndescription: An organizational safety policy governing what an agent may do without explicit authority.\nfields:\n authority:\n type: string\n required: true\nbody:\n type: text\n required: true\n---\n"
},
{
- "path": "interfaces/correspondent.interface.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: interface\nid: helio/interfaces/correspondent@1.0.0\ndisplayName: Correspondent\ndescription: Contract for agents that assess and route communications.\nrequiresSlots:\n - principal\nrequiresCapabilities:\n - helio/capabilities/triage-message@1.0.0\n"
+ "path": "context/executive-rhythm.context.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: context\nid: helio/context/executive-rhythm@1.0.0\ncontextType: helio/context-types/executive-rhythm@1.0.0\ndisplayName: Executive Rhythm\n---\n# Executive rhythm\n\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\n"
},
{
- "path": "interfaces/repository-observer.interface.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: interface\nid: helio/interfaces/repository-observer@1.0.0\ndisplayName: Repository Observer\ndescription: Contract for agents that can report evidence-backed repository status.\nrequiresSlots:\n - repository\nrequiresCapabilities:\n - helio/capabilities/repository-status@1.0.0\n"
+ "path": "context/organization.context.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: context\nid: helio/context/organization@1.0.0\ncontextType: helio/context-types/organization@1.0.0\ndisplayName: Helio Works\n---\n# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n"
},
{
- "path": "profiles/enterprise-defaults.profile.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: profile\nid: helio/profiles/enterprise-defaults@1.0.0\ndisplayName: Helio Enterprise Defaults\ndescription: Shared organizational governance for every Helio resource.\nslots:\n organization: context/organization.md\n safetyPolicy: context/safety-policy.md\nworkingNorms:\n - Preserve a clear audit trail for material decisions.\n - State uncertainty and route work to an accountable owner when authority is unclear.\ncontextFiles:\n - context/organization.md\n - context/safety-policy.md\n"
+ "path": "context/payments-service.context.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: context\nid: helio/context/payments-service@1.0.0\ncontextType: helio/context-types/payments-service@1.0.0\ndisplayName: Payments Service\n---\n# Payments service\n\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\n"
},
{
- "path": "profiles/person-defaults.profile.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: profile\nid: helio/profiles/person-defaults@1.0.0\ndisplayName: Helio Person Defaults\ndescription: Reusable behavior for respectful inbound and outbound assistance for a person.\nembeds:\n - helio/profiles/enterprise-defaults@1.0.0\nslots:\n principal: context/principal.md\nworkingNorms:\n - Distinguish drafts from messages approved for delivery.\ncontextFiles:\n - context/principal.md\nskills:\n - ref: helio/skills/triage-message@1.0.0\n"
+ "path": "context/principal.context.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: context\nid: helio/context/principal@1.0.0\ncontextType: helio/context-types/principal@1.0.0\ndisplayName: Principal\n---\n# Principal\n\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\n"
},
{
- "path": "profiles/repository-defaults.profile.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: profile\nid: helio/profiles/repository-defaults@1.0.0\ndisplayName: Helio Repository Defaults\ndescription: Reusable delivery practices for software repository assistance.\nembeds:\n - helio/profiles/enterprise-defaults@1.0.0\nslots:\n repository: context/repository.md\nworkingNorms:\n - Prefer evidence from tests, diffs, and version control over recollection.\ncontextFiles:\n - context/repository.md\nskills:\n - ref: helio/skills/repository-status@1.0.0\n"
+ "path": "context/repository.context.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: context\nid: helio/context/repository-evidence@1.0.0\ncontextType: helio/context-types/repository-evidence@1.0.0\ndisplayName: Repository Evidence\n---\n# Repository evidence\n\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\n"
},
{
- "path": "skills/payments-repository-status.skill.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: skill\nid: helio/skills/payments-repository-status@1.0.0\nbinds: helio/capabilities/repository-status@1.0.0\ndisplayName: Payments Repository Status\ndescription: Report payments-service health with contract and reconciliation evidence.\ncontextFiles:\n - context/payments-service.md\ninstructions: |\n Apply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.\n Do not report the service healthy when any required financial-control signal is unavailable.\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022focus\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022status\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022risks\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022status\u0022,\u0022evidence\u0022,\u0022risks\u0022]}\u0027\n"
+ "path": "context/safety-policy.context.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: context\nid: helio/context/safety-policy@1.0.0\ncontextType: helio/context-types/safety-policy@1.0.0\ndisplayName: Helio Safety Policy\nvalues:\n authority: explicit\n---\n# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n"
},
{
- "path": "skills/prepare-brief.skill.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: skill\nid: helio/skills/prepare-brief@1.0.0\nbinds: helio/capabilities/prepare-brief@1.0.0\ndisplayName: Prepare Brief\ndescription: Assemble an executive brief, requesting repository evidence when needed.\ncontextFiles:\n - context/executive-rhythm.md\ninstructions: |\n Build a decision-oriented brief from the supplied topic and evidence.\n When repository status is material, request \u0060payments-repo-agent.repository-status\u0060 and incorporate its returned evidence with attribution.\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022topic\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022topic\u0022],\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022brief\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022openQuestions\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022brief\u0022,\u0022openQuestions\u0022]}\u0027\n"
+ "path": "interfaces/correspondent.interface.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: interface\nid: helio/interfaces/correspondent@1.0.0\ndisplayName: Correspondent\ndescription: Contract for agents that assess and route communications.\nrequiresSlots:\n principal: helio/context-types/principal@1.0.0\nrequiresCapabilities:\n - helio/capabilities/triage-message@1.0.0\n---\n"
},
{
- "path": "skills/repository-status.skill.yaml",
- "mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: skill\nid: helio/skills/repository-status@1.0.0\nbinds: helio/capabilities/repository-status@1.0.0\ndisplayName: Repository Status\ndescription: Summarize repository health using current version-control and test evidence.\ninstructions: |\n Inspect the requested repository signals. Report branch state, recent material changes, test evidence, risks, and the next accountable action.\n Mark any unavailable signal explicitly instead of inferring success.\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022focus\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022status\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022risks\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022status\u0022,\u0022evidence\u0022,\u0022risks\u0022]}\u0027\n"
+ "path": "interfaces/repository-observer.interface.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: interface\nid: helio/interfaces/repository-observer@1.0.0\ndisplayName: Repository Observer\ndescription: Contract for agents that can report evidence-backed repository status.\nrequiresSlots:\n repository: helio/context-types/repository-evidence@1.0.0\nrequiresCapabilities:\n - helio/capabilities/repository-status@1.0.0\n---\n"
+ },
+ {
+ "path": "profiles/enterprise-defaults.profile.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: profile\nid: helio/profiles/enterprise-defaults@1.0.0\ndisplayName: Helio Enterprise Defaults\ndescription: Shared organizational governance for every Helio resource.\nslots:\n organization: helio/context/organization@1.0.0\nworkingNorms:\n - Preserve a clear audit trail for material decisions.\n - State uncertainty and route work to an accountable owner when authority is unclear.\ncontext:\n - helio/context/safety-policy@1.0.0\n---\n"
+ },
+ {
+ "path": "profiles/person-defaults.profile.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: profile\nid: helio/profiles/person-defaults@1.0.0\ndisplayName: Helio Person Defaults\ndescription: Reusable behavior for respectful inbound and outbound assistance for a person.\nembeds:\n - helio/profiles/enterprise-defaults@1.0.0\nslots:\n principal: helio/context/principal@1.0.0\nworkingNorms:\n - Distinguish drafts from messages approved for delivery.\nskills:\n - ref: helio/skills/triage-message@1.0.0\n sealed: true\n---\n"
+ },
+ {
+ "path": "profiles/repository-defaults.profile.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: profile\nid: helio/profiles/repository-defaults@1.0.0\ndisplayName: Helio Repository Defaults\ndescription: Reusable delivery practices for software repository assistance.\nembeds:\n - helio/profiles/enterprise-defaults@1.0.0\nslots:\n repository: helio/context/repository-evidence@1.0.0\nworkingNorms:\n - Prefer evidence from tests, diffs, and version control over recollection.\nskills:\n - ref: helio/skills/repository-status@1.0.0\n---\n"
+ },
+ {
+ "path": "skills/payments-repository-status.skill.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: skill\nid: helio/skills/payments-repository-status@1.0.0\nbinds: helio/capabilities/repository-status@1.0.0\ndisplayName: Payments Repository Status\ndescription: Report payments-service health with contract and reconciliation evidence.\nrequiresContextTypes:\n - helio/context-types/payments-service@1.0.0\nrequiresTools:\n - helio/tools/repository-signals@1.0.0\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022focus\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022status\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022risks\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022status\u0022,\u0022evidence\u0022,\u0022risks\u0022]}\u0027\nvariants:\n pipeline:\n instructions: |\n Apply the repository-status capability for the payments service and emit only\n the strict output object. Mark any unavailable financial-control signal as an\n explicit null; do not report the service healthy when one is missing.\n manual:\n instructions: |\n Apply the repository-status capability for the payments service, then include\n payment-contract compatibility, reconciliation checks, and rollback readiness,\n surfacing uncertainty conversationally. Do not report the service healthy when\n any required financial-control signal is unavailable.\n a2a:\n instructions: |\n Apply the repository-status capability for the payments service and return\n attributed evidence for the calling agent \u2014 contract compatibility,\n reconciliation, and rollback readiness \u2014 with no user-facing framing. Do not\n report the service healthy when any required financial-control signal is\n unavailable.\n---\n"
+ },
+ {
+ "path": "skills/prepare-brief.skill.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: skill\nid: helio/skills/prepare-brief@1.0.0\nbinds: helio/capabilities/prepare-brief@1.0.0\ndisplayName: Prepare Brief\ndescription: Assemble an executive brief, requesting repository evidence when needed.\nrequiresContextTypes:\n - helio/context-types/executive-rhythm@1.0.0\nrequiresTools:\n - helio/tools/payments-repository-agent@1.0.0\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022topic\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022topic\u0022],\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022brief\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022openQuestions\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022brief\u0022,\u0022openQuestions\u0022]}\u0027\n---\nBuild a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, use the payments repository agent tool and\nincorporate its returned evidence with attribution.\n"
+ },
+ {
+ "path": "skills/repository-status.skill.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: skill\nid: helio/skills/repository-status@1.0.0\nbinds: helio/capabilities/repository-status@1.0.0\ndisplayName: Repository Status\ndescription: Summarize repository health using current version-control and test evidence.\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022focus\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022status\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022risks\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022status\u0022,\u0022evidence\u0022,\u0022risks\u0022]}\u0027\n---\nInspect the requested repository signals. Report branch state, recent material changes, test evidence, risks, and the next accountable action.\nMark any unavailable signal explicitly instead of inferring success.\n"
+ },
+ {
+ "path": "skills/triage-message.skill.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: skill\nid: helio/skills/triage-message@1.0.0\nbinds: helio/capabilities/triage-message@1.0.0\ndisplayName: Triage Message\ndescription: Classify an inbound message and recommend an accountable next action.\nrequiresContextTypes:\n - helio/context-types/safety-policy@1.0.0\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022message\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022message\u0022],\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022summary\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022urgency\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022owner\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022nextAction\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022summary\u0022,\u0022urgency\u0022,\u0022owner\u0022,\u0022nextAction\u0022]}\u0027\n---\nRead the message and identify its sender, intent, urgency, decision owner, and requested deadline.\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\n"
+ },
+ {
+ "path": "tools/payments-repository-agent.tool.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: tool\nid: helio/tools/payments-repository-agent@1.0.0\ndisplayName: Payments Repository Agent\ndescription: Runtime import for evidence-backed payments repository status.\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022focus\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022status\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022risks\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022status\u0022,\u0022evidence\u0022,\u0022risks\u0022]}\u0027\n---\n"
},
{
- "path": "skills/triage-message.skill.yaml",
+ "path": "tools/repository-signals.tool.tfer",
+ "mediaType": "text/plain",
+ "content": "---\nschemaVersion: 4\nkind: tool\nid: helio/tools/repository-signals@1.0.0\ndisplayName: Repository Signals\ndescription: \u003E-\n Reads version-control and test signals for a repository at runtime. TypeFerence\n declares this extern; the deployer implements the body (ADR-0017, Door B).\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022repository\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022repository\u0022],\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022branch\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022tests\u0022:{\u0022type\u0022:\u0022string\u0022}}}\u0027\n---\n"
+ },
+ {
+ "path": "typeference.yaml",
"mediaType": "application/yaml",
- "content": "schemaVersion: 3\nkind: skill\nid: helio/skills/triage-message@1.0.0\nbinds: helio/capabilities/triage-message@1.0.0\ndisplayName: Triage Message\ndescription: Classify an inbound message and recommend an accountable next action.\ninstructions: |\n Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.\n Separate facts from assumptions. Return a concise recommendation; do not send a reply.\ninputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022message\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022message\u0022],\u0022additionalProperties\u0022:false}\u0027\noutputSchema: \u0027{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022summary\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022urgency\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022owner\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022nextAction\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022summary\u0022,\u0022urgency\u0022,\u0022owner\u0022,\u0022nextAction\u0022]}\u0027\n"
+ "content": "schemaVersion: 2\nname: helio/agents\nversion: 1.0.0\npublisher: helio.example\n"
}
]
},
@@ -137,37 +182,42 @@
"target": "neutral",
"agentId": "helio/executive-assistant@1.0.0",
"files": [
+ {
+ "path": ".typeference/link.json",
+ "mediaType": "application/json",
+ "content": "{\n \u0022schemaVersion\u0022: 1,\n \u0022agentId\u0022: \u0022helio/executive-assistant@1.0.0\u0022,\n \u0022target\u0022: \u0022neutral\u0022,\n \u0022defaultMode\u0022: \u0022\u0022,\n \u0022sourceDigest\u0022: \u0022sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac\u0022,\n \u0022dependencies\u0022: [],\n \u0022modes\u0022: [],\n \u0022toolImports\u0022: [\n {\n \u0022toolId\u0022: \u0022helio/tools/payments-repository-agent@1.0.0\u0022,\n \u0022skillId\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022,\n \u0022mode\u0022: \u0022*\u0022\n }\n ]\n}\n"
+ },
{
"path": "AGENTS.md",
"mediaType": "text/markdown",
- "content": "# Helio Executive Assistant\n\nCoordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Distinguish drafts from messages approved for delivery.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060context/organization.md\u0060\n- \u0060principal\u0060: \u0060context/principal.md\u0060\n- \u0060safetyPolicy\u0060: \u0060context/safety-policy.md\u0060\n\n## Available skills\n\n- \u0060executive-assistant.prepare-brief\u0060: Assemble an executive brief, requesting repository evidence when needed.\n- \u0060executive-assistant.triage-message\u0060: Classify an inbound message and recommend an accountable next action.\n\n"
+ "content": "# Helio Executive Assistant\n\nCoordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Distinguish drafts from messages approved for delivery.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060helio/context/organization@1.0.0\u0060\n- \u0060principal\u0060: \u0060helio/context/principal@1.0.0\u0060\n\n## Context\n\n### Executive Rhythm\n\n# Executive rhythm\n\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\n\n### Helio Works\n\n# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n\n### Principal\n\n# Principal\n\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\n\n### Helio Safety Policy\n\n# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n\n## Available skills\n\n- \u0060executive-assistant.prepare-brief\u0060: Assemble an executive brief, requesting repository evidence when needed.\n- \u0060executive-assistant.triage-message\u0060: Classify an inbound message and recommend an accountable next action.\n\n"
},
{
"path": "bundle.json",
"mediaType": "application/json",
- "content": "{\n \u0022id\u0022: \u0022helio/executive-assistant@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Executive Assistant\u0022,\n \u0022description\u0022: \u0022Coordinates an executive\\u0027s correspondence, briefings, and cross-agent requests.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/person-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/correspondent@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022context/organization.md\u0022,\n \u0022principal\u0022: \u0022context/principal.md\u0022,\n \u0022safetyPolicy\u0022: \u0022context/safety-policy.md\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Distinguish drafts from messages approved for delivery.\u0022\n ],\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/principal.md\u0022,\n \u0022context/executive-rhythm.md\u0022\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.prepare-brief\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022,\n \u0022description\u0022: \u0022Assemble an executive brief, requesting repository evidence when needed.\u0022,\n \u0022instructions\u0022: \u0022Build a decision-oriented brief from the supplied topic and evidence.\\nWhen repository status is material, request \\u0060payments-repo-agent.repository-status\\u0060 and incorporate its returned evidence with attribution.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022topic\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022topic\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022brief\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022openQuestions\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022brief\\u0022,\\u0022openQuestions\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/principal.md\u0022,\n \u0022context/executive-rhythm.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022\n }\n ]\n },\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.triage-message\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/triage-message@1.0.0\u0022,\n \u0022description\u0022: \u0022Classify an inbound message and recommend an accountable next action.\u0022,\n \u0022instructions\u0022: \u0022Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.\\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022message\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022message\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022summary\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022urgency\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022owner\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022nextAction\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022summary\\u0022,\\u0022urgency\\u0022,\\u0022owner\\u0022,\\u0022nextAction\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/principal.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/triage-message@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.safetyPolicy\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.principal\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/person-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/correspondent@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n }\n ]\n}\n"
+ "content": "{\n \u0022id\u0022: \u0022helio/executive-assistant@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Executive Assistant\u0022,\n \u0022description\u0022: \u0022Coordinates an executive\\u0027s correspondence, briefings, and cross-agent requests.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/person-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/correspondent@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022principal\u0022: \u0022helio/context/principal@1.0.0\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Distinguish drafts from messages approved for delivery.\u0022\n ],\n \u0022context\u0022: [\n {\n \u0022id\u0022: \u0022helio/context/executive-rhythm@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/executive-rhythm@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/executive-rhythm@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Executive rhythm\\n\\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/organization@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/organization@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Helio Works\\n\\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/principal@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/principal@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/principal@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Principal\\n\\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/safety-policy@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/safety-policy@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/safety-policy@1.0.0\u0022\n ],\n \u0022values\u0022: {\n \u0022authority\u0022: \u0022explicit\u0022\n },\n \u0022content\u0022: \u0022# Safety policy\\n\\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\\n\u0022\n }\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.prepare-brief\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022,\n \u0022description\u0022: \u0022Assemble an executive brief, requesting repository evidence when needed.\u0022,\n \u0022instructions\u0022: \u0022Build a decision-oriented brief from the supplied topic and evidence.\\nWhen repository status is material, use the payments repository agent tool and\\nincorporate its returned evidence with attribution.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022topic\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022topic\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022brief\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022openQuestions\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022brief\\u0022,\\u0022openQuestions\\u0022]}\u0022,\n \u0022requiresContextTypes\u0022: [\n \u0022helio/context-types/executive-rhythm@1.0.0\u0022\n ],\n \u0022requiresTools\u0022: [\n \u0022helio/tools/payments-repository-agent@1.0.0\u0022\n ],\n \u0022exposed\u0022: true,\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022\n }\n ]\n },\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.triage-message\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/triage-message@1.0.0\u0022,\n \u0022description\u0022: \u0022Classify an inbound message and recommend an accountable next action.\u0022,\n \u0022instructions\u0022: \u0022Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.\\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022message\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022message\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022summary\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022urgency\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022owner\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022nextAction\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022summary\\u0022,\\u0022urgency\\u0022,\\u0022owner\\u0022,\\u0022nextAction\\u0022]}\u0022,\n \u0022requiresContextTypes\u0022: [\n \u0022helio/context-types/safety-policy@1.0.0\u0022\n ],\n \u0022exposed\u0022: true,\n \u0022sealed\u0022: true,\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/triage-message@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.principal\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/person-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/correspondent@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n }\n ]\n}\n"
},
{
"path": "provenance.json",
"mediaType": "application/json",
- "content": "[\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.safetyPolicy\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.principal\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/person-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/correspondent@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n }\n]\n"
+ "content": "[\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.principal\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/person-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/correspondent@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n }\n]\n"
},
{
"path": "skills/prepare-brief/SKILL.md",
"mediaType": "text/markdown",
- "content": "---\nname: prepare-brief\ndescription: \u0022Assemble an executive brief, requesting repository evidence when needed.\u0022\n---\n\nBuild a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, request \u0060payments-repo-agent.repository-status\u0060 and incorporate its returned evidence with attribution.\n\n## Context loaded on invocation\n\n- \u0060context/organization.md\u0060\n- \u0060context/safety-policy.md\u0060\n- \u0060context/principal.md\u0060\n- \u0060context/executive-rhythm.md\u0060\n"
+ "content": "---\nname: prepare-brief\ndescription: \u0022Assemble an executive brief, requesting repository evidence when needed.\u0022\n---\n\nBuild a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, use the payments repository agent tool and\nincorporate its returned evidence with attribution.\n"
},
{
"path": "skills/triage-message/SKILL.md",
"mediaType": "text/markdown",
- "content": "---\nname: triage-message\ndescription: \u0022Classify an inbound message and recommend an accountable next action.\u0022\n---\n\nRead the message and identify its sender, intent, urgency, decision owner, and requested deadline.\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\n\n## Context loaded on invocation\n\n- \u0060context/organization.md\u0060\n- \u0060context/safety-policy.md\u0060\n- \u0060context/principal.md\u0060\n"
+ "content": "---\nname: triage-message\ndescription: \u0022Classify an inbound message and recommend an accountable next action.\u0022\n---\n\nRead the message and identify its sender, intent, urgency, decision owner, and requested deadline.\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\n"
}
]
},
"metadata": {
"generatedBy": "TypeFerence",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26",
- "sourceIdentifier": "urn:air:helio.example:typeference:source:helio",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "sourceIdentifier": "urn:air:helio.example:typeference:source:helio-agents",
"target": "neutral"
},
"trustManifest": {
@@ -176,8 +226,8 @@
"provenance": [
{
"relation": "derivedFrom",
- "sourceId": "urn:air:helio.example:typeference:source:helio",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26"
+ "sourceId": "urn:air:helio.example:typeference:source:helio-agents",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac"
}
]
}
@@ -196,32 +246,52 @@
"target": "neutral",
"agentId": "helio/payments-repo-agent@1.0.0",
"files": [
+ {
+ "path": ".typeference/link.json",
+ "mediaType": "application/json",
+ "content": "{\n \u0022schemaVersion\u0022: 1,\n \u0022agentId\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022,\n \u0022target\u0022: \u0022neutral\u0022,\n \u0022defaultMode\u0022: \u0022\u0022,\n \u0022sourceDigest\u0022: \u0022sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac\u0022,\n \u0022dependencies\u0022: [],\n \u0022modes\u0022: [\n \u0022a2a\u0022,\n \u0022manual\u0022,\n \u0022pipeline\u0022\n ],\n \u0022toolImports\u0022: [\n {\n \u0022toolId\u0022: \u0022helio/tools/repository-signals@1.0.0\u0022,\n \u0022skillId\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022,\n \u0022mode\u0022: \u0022*\u0022\n }\n ]\n}\n"
+ },
{
"path": "AGENTS.md",
"mediaType": "text/markdown",
- "content": "# Helio Payments Repository Agent\n\nSpecializes repository assistance for the fictional payments service.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Prefer evidence from tests, diffs, and version control over recollection.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060context/organization.md\u0060\n- \u0060repository\u0060: \u0060context/repository.md\u0060\n- \u0060safetyPolicy\u0060: \u0060context/safety-policy.md\u0060\n\n## Available skills\n\n- \u0060payments-repo-agent.repository-status\u0060: Report payments-service health with contract and reconciliation evidence.\n\n"
+ "content": "# Helio Payments Repository Agent\n\nSpecializes repository assistance for the fictional payments service.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Prefer evidence from tests, diffs, and version control over recollection.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060helio/context/organization@1.0.0\u0060\n- \u0060repository\u0060: \u0060helio/context/repository-evidence@1.0.0\u0060\n\n## Context\n\n### Helio Works\n\n# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n\n### Payments Service\n\n# Payments service\n\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\n\n### Repository Evidence\n\n# Repository evidence\n\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\n\n### Helio Safety Policy\n\n# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n\n## Available skills\n\n- \u0060payments-repo-agent.repository-status\u0060: Report payments-service health with contract and reconciliation evidence.\n\n"
},
{
"path": "bundle.json",
"mediaType": "application/json",
- "content": "{\n \u0022id\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Payments Repository Agent\u0022,\n \u0022description\u0022: \u0022Specializes repository assistance for the fictional payments service.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/repository-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/repository-observer@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022context/organization.md\u0022,\n \u0022repository\u0022: \u0022context/repository.md\u0022,\n \u0022safetyPolicy\u0022: \u0022context/safety-policy.md\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Prefer evidence from tests, diffs, and version control over recollection.\u0022\n ],\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/repository.md\u0022,\n \u0022context/payments-service.md\u0022\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022payments-repo-agent.repository-status\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022,\n \u0022description\u0022: \u0022Report payments-service health with contract and reconciliation evidence.\u0022,\n \u0022instructions\u0022: \u0022Apply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.\\nDo not report the service healthy when any required financial-control signal is unavailable.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022focus\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022status\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022risks\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022status\\u0022,\\u0022evidence\\u0022,\\u0022risks\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/repository.md\u0022,\n \u0022context/payments-service.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.safetyPolicy\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repository\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/repository-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/repository-observer@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n }\n ]\n}\n"
+ "content": "{\n \u0022id\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Payments Repository Agent\u0022,\n \u0022description\u0022: \u0022Specializes repository assistance for the fictional payments service.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/repository-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/repository-observer@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022repository\u0022: \u0022helio/context/repository-evidence@1.0.0\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Prefer evidence from tests, diffs, and version control over recollection.\u0022\n ],\n \u0022context\u0022: [\n {\n \u0022id\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/organization@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/organization@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Helio Works\\n\\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/payments-service@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/payments-service@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/payments-service@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Payments service\\n\\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/repository-evidence@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/repository-evidence@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/repository-evidence@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Repository evidence\\n\\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/safety-policy@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/safety-policy@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/safety-policy@1.0.0\u0022\n ],\n \u0022values\u0022: {\n \u0022authority\u0022: \u0022explicit\u0022\n },\n \u0022content\u0022: \u0022# Safety policy\\n\\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\\n\u0022\n }\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022payments-repo-agent.repository-status\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022,\n \u0022description\u0022: \u0022Report payments-service health with contract and reconciliation evidence.\u0022,\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service and emit only\\nthe strict output object. Mark any unavailable financial-control signal as an\\nexplicit null; do not report the service healthy when one is missing.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022focus\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022status\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022risks\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022status\\u0022,\\u0022evidence\\u0022,\\u0022risks\\u0022]}\u0022,\n \u0022variants\u0022: {\n \u0022a2a\u0022: {\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service and return\\nattributed evidence for the calling agent \\u2014 contract compatibility,\\nreconciliation, and rollback readiness \\u2014 with no user-facing framing. Do not\\nreport the service healthy when any required financial-control signal is\\nunavailable.\\n\u0022,\n \u0022requiresContextTypes\u0022: [],\n \u0022requiresTools\u0022: []\n },\n \u0022manual\u0022: {\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service, then include\\npayment-contract compatibility, reconciliation checks, and rollback readiness,\\nsurfacing uncertainty conversationally. Do not report the service healthy when\\nany required financial-control signal is unavailable.\\n\u0022,\n \u0022requiresContextTypes\u0022: [],\n \u0022requiresTools\u0022: []\n },\n \u0022pipeline\u0022: {\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service and emit only\\nthe strict output object. Mark any unavailable financial-control signal as an\\nexplicit null; do not report the service healthy when one is missing.\\n\u0022,\n \u0022requiresContextTypes\u0022: [],\n \u0022requiresTools\u0022: []\n }\n },\n \u0022requiresContextTypes\u0022: [\n \u0022helio/context-types/payments-service@1.0.0\u0022\n ],\n \u0022requiresTools\u0022: [\n \u0022helio/tools/repository-signals@1.0.0\u0022\n ],\n \u0022exposed\u0022: true,\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repository\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/repository-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/repository-observer@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n }\n ]\n}\n"
},
{
"path": "provenance.json",
"mediaType": "application/json",
- "content": "[\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.safetyPolicy\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repository\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/repository-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/repository-observer@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n }\n]\n"
+ "content": "[\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repository\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/repository-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/repository-observer@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n }\n]\n"
+ },
+ {
+ "path": "skills/repository-status/SKILL.a2a.md",
+ "mediaType": "text/markdown",
+ "content": "---\nname: repository-status\ndescription: \u0022Report payments-service health with contract and reconciliation evidence.\u0022\n---\n\nApply the repository-status capability for the payments service and return\nattributed evidence for the calling agent \u2014 contract compatibility,\nreconciliation, and rollback readiness \u2014 with no user-facing framing. Do not\nreport the service healthy when any required financial-control signal is\nunavailable.\n"
+ },
+ {
+ "path": "skills/repository-status/SKILL.manual.md",
+ "mediaType": "text/markdown",
+ "content": "---\nname: repository-status\ndescription: \u0022Report payments-service health with contract and reconciliation evidence.\u0022\n---\n\nApply the repository-status capability for the payments service, then include\npayment-contract compatibility, reconciliation checks, and rollback readiness,\nsurfacing uncertainty conversationally. Do not report the service healthy when\nany required financial-control signal is unavailable.\n"
},
{
"path": "skills/repository-status/SKILL.md",
"mediaType": "text/markdown",
- "content": "---\nname: repository-status\ndescription: \u0022Report payments-service health with contract and reconciliation evidence.\u0022\n---\n\nApply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.\nDo not report the service healthy when any required financial-control signal is unavailable.\n\n## Context loaded on invocation\n\n- \u0060context/organization.md\u0060\n- \u0060context/safety-policy.md\u0060\n- \u0060context/repository.md\u0060\n- \u0060context/payments-service.md\u0060\n"
+ "content": "---\nname: repository-status\ndescription: \u0022Report payments-service health with contract and reconciliation evidence.\u0022\n---\n\nApply the repository-status capability for the payments service and emit only\nthe strict output object. Mark any unavailable financial-control signal as an\nexplicit null; do not report the service healthy when one is missing.\n"
+ },
+ {
+ "path": "skills/repository-status/SKILL.pipeline.md",
+ "mediaType": "text/markdown",
+ "content": "---\nname: repository-status\ndescription: \u0022Report payments-service health with contract and reconciliation evidence.\u0022\n---\n\nApply the repository-status capability for the payments service and emit only\nthe strict output object. Mark any unavailable financial-control signal as an\nexplicit null; do not report the service healthy when one is missing.\n"
}
]
},
"metadata": {
"generatedBy": "TypeFerence",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26",
- "sourceIdentifier": "urn:air:helio.example:typeference:source:helio",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "sourceIdentifier": "urn:air:helio.example:typeference:source:helio-agents",
"target": "neutral"
},
"trustManifest": {
@@ -230,8 +300,8 @@
"provenance": [
{
"relation": "derivedFrom",
- "sourceId": "urn:air:helio.example:typeference:source:helio",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26"
+ "sourceId": "urn:air:helio.example:typeference:source:helio-agents",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac"
}
]
}
@@ -254,34 +324,34 @@
{
"path": ".agents/skills/prepare-brief/SKILL.md",
"mediaType": "text/markdown",
- "content": "---\nname: prepare-brief\ndescription: \u0022Assemble an executive brief, requesting repository evidence when needed.\u0022\n---\n\nBuild a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, request \u0060payments-repo-agent.repository-status\u0060 and incorporate its returned evidence with attribution.\n\n## Context loaded on invocation\n\n- \u0060context/organization.md\u0060\n- \u0060context/safety-policy.md\u0060\n- \u0060context/principal.md\u0060\n- \u0060context/executive-rhythm.md\u0060\n"
+ "content": "---\nname: prepare-brief\ndescription: \u0022Assemble an executive brief, requesting repository evidence when needed.\u0022\n---\n\nBuild a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, use the payments repository agent tool and\nincorporate its returned evidence with attribution.\n"
},
{
"path": ".agents/skills/triage-message/SKILL.md",
"mediaType": "text/markdown",
- "content": "---\nname: triage-message\ndescription: \u0022Classify an inbound message and recommend an accountable next action.\u0022\n---\n\nRead the message and identify its sender, intent, urgency, decision owner, and requested deadline.\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\n\n## Context loaded on invocation\n\n- \u0060context/organization.md\u0060\n- \u0060context/safety-policy.md\u0060\n- \u0060context/principal.md\u0060\n"
+ "content": "---\nname: triage-message\ndescription: \u0022Classify an inbound message and recommend an accountable next action.\u0022\n---\n\nRead the message and identify its sender, intent, urgency, decision owner, and requested deadline.\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\n"
},
{
- "path": ".codex/config.toml",
- "mediaType": "application/toml",
- "content": "[mcp_servers.typeference]\ncommand = \u0022typeference\u0022\nargs = [\u0022serve\u0022, \u0022.typeference\u0022]\n"
+ "path": ".typeference/bundle.json",
+ "mediaType": "application/json",
+ "content": "{\n \u0022id\u0022: \u0022helio/executive-assistant@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Executive Assistant\u0022,\n \u0022description\u0022: \u0022Coordinates an executive\\u0027s correspondence, briefings, and cross-agent requests.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/person-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/correspondent@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022principal\u0022: \u0022helio/context/principal@1.0.0\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Distinguish drafts from messages approved for delivery.\u0022\n ],\n \u0022context\u0022: [\n {\n \u0022id\u0022: \u0022helio/context/executive-rhythm@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/executive-rhythm@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/executive-rhythm@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Executive rhythm\\n\\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/organization@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/organization@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Helio Works\\n\\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/principal@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/principal@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/principal@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Principal\\n\\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/safety-policy@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/safety-policy@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/safety-policy@1.0.0\u0022\n ],\n \u0022values\u0022: {\n \u0022authority\u0022: \u0022explicit\u0022\n },\n \u0022content\u0022: \u0022# Safety policy\\n\\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\\n\u0022\n }\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.prepare-brief\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022,\n \u0022description\u0022: \u0022Assemble an executive brief, requesting repository evidence when needed.\u0022,\n \u0022instructions\u0022: \u0022Build a decision-oriented brief from the supplied topic and evidence.\\nWhen repository status is material, use the payments repository agent tool and\\nincorporate its returned evidence with attribution.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022topic\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022topic\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022brief\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022openQuestions\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022brief\\u0022,\\u0022openQuestions\\u0022]}\u0022,\n \u0022requiresContextTypes\u0022: [\n \u0022helio/context-types/executive-rhythm@1.0.0\u0022\n ],\n \u0022requiresTools\u0022: [\n \u0022helio/tools/payments-repository-agent@1.0.0\u0022\n ],\n \u0022exposed\u0022: true,\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022\n }\n ]\n },\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.triage-message\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/triage-message@1.0.0\u0022,\n \u0022description\u0022: \u0022Classify an inbound message and recommend an accountable next action.\u0022,\n \u0022instructions\u0022: \u0022Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.\\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022message\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022message\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022summary\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022urgency\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022owner\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022nextAction\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022summary\\u0022,\\u0022urgency\\u0022,\\u0022owner\\u0022,\\u0022nextAction\\u0022]}\u0022,\n \u0022requiresContextTypes\u0022: [\n \u0022helio/context-types/safety-policy@1.0.0\u0022\n ],\n \u0022exposed\u0022: true,\n \u0022sealed\u0022: true,\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/triage-message@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.principal\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/person-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/correspondent@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n }\n ]\n}\n"
},
{
- "path": ".typeference/bundle.json",
+ "path": ".typeference/link.json",
"mediaType": "application/json",
- "content": "{\n \u0022id\u0022: \u0022helio/executive-assistant@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Executive Assistant\u0022,\n \u0022description\u0022: \u0022Coordinates an executive\\u0027s correspondence, briefings, and cross-agent requests.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/person-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/correspondent@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022context/organization.md\u0022,\n \u0022principal\u0022: \u0022context/principal.md\u0022,\n \u0022safetyPolicy\u0022: \u0022context/safety-policy.md\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Distinguish drafts from messages approved for delivery.\u0022\n ],\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/principal.md\u0022,\n \u0022context/executive-rhythm.md\u0022\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.prepare-brief\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022,\n \u0022description\u0022: \u0022Assemble an executive brief, requesting repository evidence when needed.\u0022,\n \u0022instructions\u0022: \u0022Build a decision-oriented brief from the supplied topic and evidence.\\nWhen repository status is material, request \\u0060payments-repo-agent.repository-status\\u0060 and incorporate its returned evidence with attribution.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022topic\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022topic\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022brief\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022openQuestions\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022brief\\u0022,\\u0022openQuestions\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/principal.md\u0022,\n \u0022context/executive-rhythm.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022\n }\n ]\n },\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.triage-message\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/triage-message@1.0.0\u0022,\n \u0022description\u0022: \u0022Classify an inbound message and recommend an accountable next action.\u0022,\n \u0022instructions\u0022: \u0022Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.\\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022message\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022message\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022summary\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022urgency\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022owner\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022nextAction\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022summary\\u0022,\\u0022urgency\\u0022,\\u0022owner\\u0022,\\u0022nextAction\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/principal.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/triage-message@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.safetyPolicy\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.principal\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/person-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/correspondent@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n }\n ]\n}\n"
+ "content": "{\n \u0022schemaVersion\u0022: 1,\n \u0022agentId\u0022: \u0022helio/executive-assistant@1.0.0\u0022,\n \u0022target\u0022: \u0022codex\u0022,\n \u0022defaultMode\u0022: \u0022manual\u0022,\n \u0022sourceDigest\u0022: \u0022sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac\u0022,\n \u0022dependencies\u0022: [],\n \u0022modes\u0022: [],\n \u0022toolImports\u0022: [\n {\n \u0022toolId\u0022: \u0022helio/tools/payments-repository-agent@1.0.0\u0022,\n \u0022skillId\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022,\n \u0022mode\u0022: \u0022*\u0022\n }\n ]\n}\n"
},
{
"path": "AGENTS.md",
"mediaType": "text/markdown",
- "content": "# Helio Executive Assistant\n\nCoordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Distinguish drafts from messages approved for delivery.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060context/organization.md\u0060\n- \u0060principal\u0060: \u0060context/principal.md\u0060\n- \u0060safetyPolicy\u0060: \u0060context/safety-policy.md\u0060\n\n## Available skills\n\n- \u0060executive-assistant.prepare-brief\u0060: Assemble an executive brief, requesting repository evidence when needed.\n- \u0060executive-assistant.triage-message\u0060: Classify an inbound message and recommend an accountable next action.\n\n"
+ "content": "# Helio Executive Assistant\n\nCoordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Distinguish drafts from messages approved for delivery.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060helio/context/organization@1.0.0\u0060\n- \u0060principal\u0060: \u0060helio/context/principal@1.0.0\u0060\n\n## Context\n\n### Executive Rhythm\n\n# Executive rhythm\n\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\n\n### Helio Works\n\n# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n\n### Principal\n\n# Principal\n\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\n\n### Helio Safety Policy\n\n# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n\n## Available skills\n\n- \u0060executive-assistant.prepare-brief\u0060: Assemble an executive brief, requesting repository evidence when needed.\n- \u0060executive-assistant.triage-message\u0060: Classify an inbound message and recommend an accountable next action.\n\n"
}
]
},
"metadata": {
"generatedBy": "TypeFerence",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26",
- "sourceIdentifier": "urn:air:helio.example:typeference:source:helio",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "sourceIdentifier": "urn:air:helio.example:typeference:source:helio-agents",
"target": "codex"
},
"trustManifest": {
@@ -290,8 +360,8 @@
"provenance": [
{
"relation": "derivedFrom",
- "sourceId": "urn:air:helio.example:typeference:source:helio",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26"
+ "sourceId": "urn:air:helio.example:typeference:source:helio-agents",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac"
}
]
}
@@ -313,29 +383,29 @@
{
"path": ".agents/skills/repository-status/SKILL.md",
"mediaType": "text/markdown",
- "content": "---\nname: repository-status\ndescription: \u0022Report payments-service health with contract and reconciliation evidence.\u0022\n---\n\nApply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.\nDo not report the service healthy when any required financial-control signal is unavailable.\n\n## Context loaded on invocation\n\n- \u0060context/organization.md\u0060\n- \u0060context/safety-policy.md\u0060\n- \u0060context/repository.md\u0060\n- \u0060context/payments-service.md\u0060\n"
+ "content": "---\nname: repository-status\ndescription: \u0022Report payments-service health with contract and reconciliation evidence.\u0022\n---\n\nApply the repository-status capability for the payments service, then include\npayment-contract compatibility, reconciliation checks, and rollback readiness,\nsurfacing uncertainty conversationally. Do not report the service healthy when\nany required financial-control signal is unavailable.\n"
},
{
- "path": ".codex/config.toml",
- "mediaType": "application/toml",
- "content": "[mcp_servers.typeference]\ncommand = \u0022typeference\u0022\nargs = [\u0022serve\u0022, \u0022.typeference\u0022]\n"
+ "path": ".typeference/bundle.json",
+ "mediaType": "application/json",
+ "content": "{\n \u0022id\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Payments Repository Agent\u0022,\n \u0022description\u0022: \u0022Specializes repository assistance for the fictional payments service.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/repository-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/repository-observer@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022repository\u0022: \u0022helio/context/repository-evidence@1.0.0\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Prefer evidence from tests, diffs, and version control over recollection.\u0022\n ],\n \u0022context\u0022: [\n {\n \u0022id\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/organization@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/organization@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Helio Works\\n\\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/payments-service@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/payments-service@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/payments-service@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Payments service\\n\\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/repository-evidence@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/repository-evidence@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/repository-evidence@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Repository evidence\\n\\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/safety-policy@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/safety-policy@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/safety-policy@1.0.0\u0022\n ],\n \u0022values\u0022: {\n \u0022authority\u0022: \u0022explicit\u0022\n },\n \u0022content\u0022: \u0022# Safety policy\\n\\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\\n\u0022\n }\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022payments-repo-agent.repository-status\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022,\n \u0022description\u0022: \u0022Report payments-service health with contract and reconciliation evidence.\u0022,\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service and emit only\\nthe strict output object. Mark any unavailable financial-control signal as an\\nexplicit null; do not report the service healthy when one is missing.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022focus\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022status\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022risks\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022status\\u0022,\\u0022evidence\\u0022,\\u0022risks\\u0022]}\u0022,\n \u0022variants\u0022: {\n \u0022a2a\u0022: {\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service and return\\nattributed evidence for the calling agent \\u2014 contract compatibility,\\nreconciliation, and rollback readiness \\u2014 with no user-facing framing. Do not\\nreport the service healthy when any required financial-control signal is\\nunavailable.\\n\u0022,\n \u0022requiresContextTypes\u0022: [],\n \u0022requiresTools\u0022: []\n },\n \u0022manual\u0022: {\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service, then include\\npayment-contract compatibility, reconciliation checks, and rollback readiness,\\nsurfacing uncertainty conversationally. Do not report the service healthy when\\nany required financial-control signal is unavailable.\\n\u0022,\n \u0022requiresContextTypes\u0022: [],\n \u0022requiresTools\u0022: []\n },\n \u0022pipeline\u0022: {\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service and emit only\\nthe strict output object. Mark any unavailable financial-control signal as an\\nexplicit null; do not report the service healthy when one is missing.\\n\u0022,\n \u0022requiresContextTypes\u0022: [],\n \u0022requiresTools\u0022: []\n }\n },\n \u0022requiresContextTypes\u0022: [\n \u0022helio/context-types/payments-service@1.0.0\u0022\n ],\n \u0022requiresTools\u0022: [\n \u0022helio/tools/repository-signals@1.0.0\u0022\n ],\n \u0022exposed\u0022: true,\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repository\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/repository-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/repository-observer@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n }\n ]\n}\n"
},
{
- "path": ".typeference/bundle.json",
+ "path": ".typeference/link.json",
"mediaType": "application/json",
- "content": "{\n \u0022id\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Payments Repository Agent\u0022,\n \u0022description\u0022: \u0022Specializes repository assistance for the fictional payments service.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/repository-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/repository-observer@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022context/organization.md\u0022,\n \u0022repository\u0022: \u0022context/repository.md\u0022,\n \u0022safetyPolicy\u0022: \u0022context/safety-policy.md\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Prefer evidence from tests, diffs, and version control over recollection.\u0022\n ],\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/repository.md\u0022,\n \u0022context/payments-service.md\u0022\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022payments-repo-agent.repository-status\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022,\n \u0022description\u0022: \u0022Report payments-service health with contract and reconciliation evidence.\u0022,\n \u0022instructions\u0022: \u0022Apply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.\\nDo not report the service healthy when any required financial-control signal is unavailable.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022focus\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022status\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022risks\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022status\\u0022,\\u0022evidence\\u0022,\\u0022risks\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/repository.md\u0022,\n \u0022context/payments-service.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.safetyPolicy\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repository\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/repository-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/repository-observer@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n }\n ]\n}\n"
+ "content": "{\n \u0022schemaVersion\u0022: 1,\n \u0022agentId\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022,\n \u0022target\u0022: \u0022codex\u0022,\n \u0022defaultMode\u0022: \u0022manual\u0022,\n \u0022sourceDigest\u0022: \u0022sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac\u0022,\n \u0022dependencies\u0022: [],\n \u0022modes\u0022: [\n \u0022a2a\u0022,\n \u0022manual\u0022,\n \u0022pipeline\u0022\n ],\n \u0022toolImports\u0022: [\n {\n \u0022toolId\u0022: \u0022helio/tools/repository-signals@1.0.0\u0022,\n \u0022skillId\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022,\n \u0022mode\u0022: \u0022*\u0022\n }\n ]\n}\n"
},
{
"path": "AGENTS.md",
"mediaType": "text/markdown",
- "content": "# Helio Payments Repository Agent\n\nSpecializes repository assistance for the fictional payments service.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Prefer evidence from tests, diffs, and version control over recollection.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060context/organization.md\u0060\n- \u0060repository\u0060: \u0060context/repository.md\u0060\n- \u0060safetyPolicy\u0060: \u0060context/safety-policy.md\u0060\n\n## Available skills\n\n- \u0060payments-repo-agent.repository-status\u0060: Report payments-service health with contract and reconciliation evidence.\n\n"
+ "content": "# Helio Payments Repository Agent\n\nSpecializes repository assistance for the fictional payments service.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Prefer evidence from tests, diffs, and version control over recollection.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060helio/context/organization@1.0.0\u0060\n- \u0060repository\u0060: \u0060helio/context/repository-evidence@1.0.0\u0060\n\n## Context\n\n### Helio Works\n\n# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n\n### Payments Service\n\n# Payments service\n\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\n\n### Repository Evidence\n\n# Repository evidence\n\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\n\n### Helio Safety Policy\n\n# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n\n## Available skills\n\n- \u0060payments-repo-agent.repository-status\u0060: Report payments-service health with contract and reconciliation evidence.\n\n"
}
]
},
"metadata": {
"generatedBy": "TypeFerence",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26",
- "sourceIdentifier": "urn:air:helio.example:typeference:source:helio",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "sourceIdentifier": "urn:air:helio.example:typeference:source:helio-agents",
"target": "codex"
},
"trustManifest": {
@@ -344,8 +414,8 @@
"provenance": [
{
"relation": "derivedFrom",
- "sourceId": "urn:air:helio.example:typeference:source:helio",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26"
+ "sourceId": "urn:air:helio.example:typeference:source:helio-agents",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac"
}
]
}
@@ -368,24 +438,29 @@
{
"path": ".github/agents/executive-assistant.agent.md",
"mediaType": "text/markdown",
- "content": "---\nname: executive-assistant\ndescription: \u0022Coordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\u0022\n---\n\n# Helio Executive Assistant\n\nCoordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Distinguish drafts from messages approved for delivery.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060context/organization.md\u0060\n- \u0060principal\u0060: \u0060context/principal.md\u0060\n- \u0060safetyPolicy\u0060: \u0060context/safety-policy.md\u0060\n\n## Available skills\n\n- \u0060executive-assistant.prepare-brief\u0060: Assemble an executive brief, requesting repository evidence when needed.\n- \u0060executive-assistant.triage-message\u0060: Classify an inbound message and recommend an accountable next action.\n\n"
+ "content": "---\nname: executive-assistant\ndescription: \u0022Coordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\u0022\n---\n\n# Helio Executive Assistant\n\nCoordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Distinguish drafts from messages approved for delivery.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060helio/context/organization@1.0.0\u0060\n- \u0060principal\u0060: \u0060helio/context/principal@1.0.0\u0060\n\n## Context\n\n### Executive Rhythm\n\n# Executive rhythm\n\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\n\n### Helio Works\n\n# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n\n### Principal\n\n# Principal\n\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\n\n### Helio Safety Policy\n\n# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n\n## Available skills\n\n- \u0060executive-assistant.prepare-brief\u0060: Assemble an executive brief, requesting repository evidence when needed.\n\nBuild a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, use the payments repository agent tool and\nincorporate its returned evidence with attribution.\n\n- \u0060executive-assistant.triage-message\u0060: Classify an inbound message and recommend an accountable next action.\n\nRead the message and identify its sender, intent, urgency, decision owner, and requested deadline.\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\n\n\n"
},
{
"path": ".github/copilot-instructions.md",
"mediaType": "text/markdown",
- "content": "# Helio Executive Assistant\n\nCoordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Distinguish drafts from messages approved for delivery.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060context/organization.md\u0060\n- \u0060principal\u0060: \u0060context/principal.md\u0060\n- \u0060safetyPolicy\u0060: \u0060context/safety-policy.md\u0060\n\n## Available skills\n\n- \u0060executive-assistant.prepare-brief\u0060: Assemble an executive brief, requesting repository evidence when needed.\n- \u0060executive-assistant.triage-message\u0060: Classify an inbound message and recommend an accountable next action.\n\n"
+ "content": "# Helio Executive Assistant\n\nCoordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Distinguish drafts from messages approved for delivery.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060helio/context/organization@1.0.0\u0060\n- \u0060principal\u0060: \u0060helio/context/principal@1.0.0\u0060\n\n## Context\n\n### Executive Rhythm\n\n# Executive rhythm\n\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\n\n### Helio Works\n\n# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n\n### Principal\n\n# Principal\n\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\n\n### Helio Safety Policy\n\n# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n\n## Available skills\n\n- \u0060executive-assistant.prepare-brief\u0060: Assemble an executive brief, requesting repository evidence when needed.\n\nBuild a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, use the payments repository agent tool and\nincorporate its returned evidence with attribution.\n\n- \u0060executive-assistant.triage-message\u0060: Classify an inbound message and recommend an accountable next action.\n\nRead the message and identify its sender, intent, urgency, decision owner, and requested deadline.\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\n\n\n"
},
{
"path": ".typeference/bundle.json",
"mediaType": "application/json",
- "content": "{\n \u0022id\u0022: \u0022helio/executive-assistant@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Executive Assistant\u0022,\n \u0022description\u0022: \u0022Coordinates an executive\\u0027s correspondence, briefings, and cross-agent requests.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/person-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/correspondent@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022context/organization.md\u0022,\n \u0022principal\u0022: \u0022context/principal.md\u0022,\n \u0022safetyPolicy\u0022: \u0022context/safety-policy.md\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Distinguish drafts from messages approved for delivery.\u0022\n ],\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/principal.md\u0022,\n \u0022context/executive-rhythm.md\u0022\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.prepare-brief\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022,\n \u0022description\u0022: \u0022Assemble an executive brief, requesting repository evidence when needed.\u0022,\n \u0022instructions\u0022: \u0022Build a decision-oriented brief from the supplied topic and evidence.\\nWhen repository status is material, request \\u0060payments-repo-agent.repository-status\\u0060 and incorporate its returned evidence with attribution.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022topic\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022topic\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022brief\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022openQuestions\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022brief\\u0022,\\u0022openQuestions\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/principal.md\u0022,\n \u0022context/executive-rhythm.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022\n }\n ]\n },\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.triage-message\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/triage-message@1.0.0\u0022,\n \u0022description\u0022: \u0022Classify an inbound message and recommend an accountable next action.\u0022,\n \u0022instructions\u0022: \u0022Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.\\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022message\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022message\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022summary\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022urgency\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022owner\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022nextAction\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022summary\\u0022,\\u0022urgency\\u0022,\\u0022owner\\u0022,\\u0022nextAction\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/principal.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/triage-message@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.safetyPolicy\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.principal\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/person-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/correspondent@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n }\n ]\n}\n"
+ "content": "{\n \u0022id\u0022: \u0022helio/executive-assistant@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Executive Assistant\u0022,\n \u0022description\u0022: \u0022Coordinates an executive\\u0027s correspondence, briefings, and cross-agent requests.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/person-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/correspondent@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022principal\u0022: \u0022helio/context/principal@1.0.0\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Distinguish drafts from messages approved for delivery.\u0022\n ],\n \u0022context\u0022: [\n {\n \u0022id\u0022: \u0022helio/context/executive-rhythm@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/executive-rhythm@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/executive-rhythm@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Executive rhythm\\n\\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/organization@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/organization@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Helio Works\\n\\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/principal@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/principal@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/principal@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Principal\\n\\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/safety-policy@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/safety-policy@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/safety-policy@1.0.0\u0022\n ],\n \u0022values\u0022: {\n \u0022authority\u0022: \u0022explicit\u0022\n },\n \u0022content\u0022: \u0022# Safety policy\\n\\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\\n\u0022\n }\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.prepare-brief\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022,\n \u0022description\u0022: \u0022Assemble an executive brief, requesting repository evidence when needed.\u0022,\n \u0022instructions\u0022: \u0022Build a decision-oriented brief from the supplied topic and evidence.\\nWhen repository status is material, use the payments repository agent tool and\\nincorporate its returned evidence with attribution.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022topic\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022topic\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022brief\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022openQuestions\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022brief\\u0022,\\u0022openQuestions\\u0022]}\u0022,\n \u0022requiresContextTypes\u0022: [\n \u0022helio/context-types/executive-rhythm@1.0.0\u0022\n ],\n \u0022requiresTools\u0022: [\n \u0022helio/tools/payments-repository-agent@1.0.0\u0022\n ],\n \u0022exposed\u0022: true,\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022\n }\n ]\n },\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.triage-message\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/triage-message@1.0.0\u0022,\n \u0022description\u0022: \u0022Classify an inbound message and recommend an accountable next action.\u0022,\n \u0022instructions\u0022: \u0022Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.\\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022message\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022message\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022summary\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022urgency\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022owner\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022nextAction\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022summary\\u0022,\\u0022urgency\\u0022,\\u0022owner\\u0022,\\u0022nextAction\\u0022]}\u0022,\n \u0022requiresContextTypes\u0022: [\n \u0022helio/context-types/safety-policy@1.0.0\u0022\n ],\n \u0022exposed\u0022: true,\n \u0022sealed\u0022: true,\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/triage-message@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.principal\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/person-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/correspondent@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n }\n ]\n}\n"
+ },
+ {
+ "path": ".typeference/link.json",
+ "mediaType": "application/json",
+ "content": "{\n \u0022schemaVersion\u0022: 1,\n \u0022agentId\u0022: \u0022helio/executive-assistant@1.0.0\u0022,\n \u0022target\u0022: \u0022copilot\u0022,\n \u0022defaultMode\u0022: \u0022manual\u0022,\n \u0022sourceDigest\u0022: \u0022sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac\u0022,\n \u0022dependencies\u0022: [],\n \u0022modes\u0022: [],\n \u0022toolImports\u0022: [\n {\n \u0022toolId\u0022: \u0022helio/tools/payments-repository-agent@1.0.0\u0022,\n \u0022skillId\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022,\n \u0022mode\u0022: \u0022*\u0022\n }\n ]\n}\n"
}
]
},
"metadata": {
"generatedBy": "TypeFerence",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26",
- "sourceIdentifier": "urn:air:helio.example:typeference:source:helio",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "sourceIdentifier": "urn:air:helio.example:typeference:source:helio-agents",
"target": "copilot"
},
"trustManifest": {
@@ -394,8 +469,8 @@
"provenance": [
{
"relation": "derivedFrom",
- "sourceId": "urn:air:helio.example:typeference:source:helio",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26"
+ "sourceId": "urn:air:helio.example:typeference:source:helio-agents",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac"
}
]
}
@@ -417,24 +492,29 @@
{
"path": ".github/agents/payments-repo-agent.agent.md",
"mediaType": "text/markdown",
- "content": "---\nname: payments-repo-agent\ndescription: \u0022Specializes repository assistance for the fictional payments service.\u0022\n---\n\n# Helio Payments Repository Agent\n\nSpecializes repository assistance for the fictional payments service.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Prefer evidence from tests, diffs, and version control over recollection.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060context/organization.md\u0060\n- \u0060repository\u0060: \u0060context/repository.md\u0060\n- \u0060safetyPolicy\u0060: \u0060context/safety-policy.md\u0060\n\n## Available skills\n\n- \u0060payments-repo-agent.repository-status\u0060: Report payments-service health with contract and reconciliation evidence.\n\n"
+ "content": "---\nname: payments-repo-agent\ndescription: \u0022Specializes repository assistance for the fictional payments service.\u0022\n---\n\n# Helio Payments Repository Agent\n\nSpecializes repository assistance for the fictional payments service.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Prefer evidence from tests, diffs, and version control over recollection.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060helio/context/organization@1.0.0\u0060\n- \u0060repository\u0060: \u0060helio/context/repository-evidence@1.0.0\u0060\n\n## Context\n\n### Helio Works\n\n# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n\n### Payments Service\n\n# Payments service\n\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\n\n### Repository Evidence\n\n# Repository evidence\n\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\n\n### Helio Safety Policy\n\n# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n\n## Available skills\n\n- \u0060payments-repo-agent.repository-status\u0060: Report payments-service health with contract and reconciliation evidence.\n\nApply the repository-status capability for the payments service, then include\npayment-contract compatibility, reconciliation checks, and rollback readiness,\nsurfacing uncertainty conversationally. Do not report the service healthy when\nany required financial-control signal is unavailable.\n\n\n"
},
{
"path": ".github/copilot-instructions.md",
"mediaType": "text/markdown",
- "content": "# Helio Payments Repository Agent\n\nSpecializes repository assistance for the fictional payments service.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Prefer evidence from tests, diffs, and version control over recollection.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060context/organization.md\u0060\n- \u0060repository\u0060: \u0060context/repository.md\u0060\n- \u0060safetyPolicy\u0060: \u0060context/safety-policy.md\u0060\n\n## Available skills\n\n- \u0060payments-repo-agent.repository-status\u0060: Report payments-service health with contract and reconciliation evidence.\n\n"
+ "content": "# Helio Payments Repository Agent\n\nSpecializes repository assistance for the fictional payments service.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Prefer evidence from tests, diffs, and version control over recollection.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060helio/context/organization@1.0.0\u0060\n- \u0060repository\u0060: \u0060helio/context/repository-evidence@1.0.0\u0060\n\n## Context\n\n### Helio Works\n\n# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n\n### Payments Service\n\n# Payments service\n\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\n\n### Repository Evidence\n\n# Repository evidence\n\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\n\n### Helio Safety Policy\n\n# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n\n## Available skills\n\n- \u0060payments-repo-agent.repository-status\u0060: Report payments-service health with contract and reconciliation evidence.\n\nApply the repository-status capability for the payments service, then include\npayment-contract compatibility, reconciliation checks, and rollback readiness,\nsurfacing uncertainty conversationally. Do not report the service healthy when\nany required financial-control signal is unavailable.\n\n\n"
},
{
"path": ".typeference/bundle.json",
"mediaType": "application/json",
- "content": "{\n \u0022id\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Payments Repository Agent\u0022,\n \u0022description\u0022: \u0022Specializes repository assistance for the fictional payments service.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/repository-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/repository-observer@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022context/organization.md\u0022,\n \u0022repository\u0022: \u0022context/repository.md\u0022,\n \u0022safetyPolicy\u0022: \u0022context/safety-policy.md\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Prefer evidence from tests, diffs, and version control over recollection.\u0022\n ],\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/repository.md\u0022,\n \u0022context/payments-service.md\u0022\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022payments-repo-agent.repository-status\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022,\n \u0022description\u0022: \u0022Report payments-service health with contract and reconciliation evidence.\u0022,\n \u0022instructions\u0022: \u0022Apply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.\\nDo not report the service healthy when any required financial-control signal is unavailable.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022focus\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022status\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022risks\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022status\\u0022,\\u0022evidence\\u0022,\\u0022risks\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/repository.md\u0022,\n \u0022context/payments-service.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.safetyPolicy\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repository\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/repository-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/repository-observer@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n }\n ]\n}\n"
+ "content": "{\n \u0022id\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Payments Repository Agent\u0022,\n \u0022description\u0022: \u0022Specializes repository assistance for the fictional payments service.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/repository-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/repository-observer@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022repository\u0022: \u0022helio/context/repository-evidence@1.0.0\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Prefer evidence from tests, diffs, and version control over recollection.\u0022\n ],\n \u0022context\u0022: [\n {\n \u0022id\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/organization@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/organization@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Helio Works\\n\\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/payments-service@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/payments-service@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/payments-service@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Payments service\\n\\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/repository-evidence@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/repository-evidence@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/repository-evidence@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Repository evidence\\n\\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/safety-policy@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/safety-policy@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/safety-policy@1.0.0\u0022\n ],\n \u0022values\u0022: {\n \u0022authority\u0022: \u0022explicit\u0022\n },\n \u0022content\u0022: \u0022# Safety policy\\n\\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\\n\u0022\n }\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022payments-repo-agent.repository-status\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022,\n \u0022description\u0022: \u0022Report payments-service health with contract and reconciliation evidence.\u0022,\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service and emit only\\nthe strict output object. Mark any unavailable financial-control signal as an\\nexplicit null; do not report the service healthy when one is missing.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022focus\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022status\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022risks\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022status\\u0022,\\u0022evidence\\u0022,\\u0022risks\\u0022]}\u0022,\n \u0022variants\u0022: {\n \u0022a2a\u0022: {\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service and return\\nattributed evidence for the calling agent \\u2014 contract compatibility,\\nreconciliation, and rollback readiness \\u2014 with no user-facing framing. Do not\\nreport the service healthy when any required financial-control signal is\\nunavailable.\\n\u0022,\n \u0022requiresContextTypes\u0022: [],\n \u0022requiresTools\u0022: []\n },\n \u0022manual\u0022: {\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service, then include\\npayment-contract compatibility, reconciliation checks, and rollback readiness,\\nsurfacing uncertainty conversationally. Do not report the service healthy when\\nany required financial-control signal is unavailable.\\n\u0022,\n \u0022requiresContextTypes\u0022: [],\n \u0022requiresTools\u0022: []\n },\n \u0022pipeline\u0022: {\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service and emit only\\nthe strict output object. Mark any unavailable financial-control signal as an\\nexplicit null; do not report the service healthy when one is missing.\\n\u0022,\n \u0022requiresContextTypes\u0022: [],\n \u0022requiresTools\u0022: []\n }\n },\n \u0022requiresContextTypes\u0022: [\n \u0022helio/context-types/payments-service@1.0.0\u0022\n ],\n \u0022requiresTools\u0022: [\n \u0022helio/tools/repository-signals@1.0.0\u0022\n ],\n \u0022exposed\u0022: true,\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repository\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/repository-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/repository-observer@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n }\n ]\n}\n"
+ },
+ {
+ "path": ".typeference/link.json",
+ "mediaType": "application/json",
+ "content": "{\n \u0022schemaVersion\u0022: 1,\n \u0022agentId\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022,\n \u0022target\u0022: \u0022copilot\u0022,\n \u0022defaultMode\u0022: \u0022manual\u0022,\n \u0022sourceDigest\u0022: \u0022sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac\u0022,\n \u0022dependencies\u0022: [],\n \u0022modes\u0022: [\n \u0022a2a\u0022,\n \u0022manual\u0022,\n \u0022pipeline\u0022\n ],\n \u0022toolImports\u0022: [\n {\n \u0022toolId\u0022: \u0022helio/tools/repository-signals@1.0.0\u0022,\n \u0022skillId\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022,\n \u0022mode\u0022: \u0022*\u0022\n }\n ]\n}\n"
}
]
},
"metadata": {
"generatedBy": "TypeFerence",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26",
- "sourceIdentifier": "urn:air:helio.example:typeference:source:helio",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "sourceIdentifier": "urn:air:helio.example:typeference:source:helio-agents",
"target": "copilot"
},
"trustManifest": {
@@ -443,8 +523,8 @@
"provenance": [
{
"relation": "derivedFrom",
- "sourceId": "urn:air:helio.example:typeference:source:helio",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26"
+ "sourceId": "urn:air:helio.example:typeference:source:helio-agents",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac"
}
]
}
@@ -467,24 +547,29 @@
{
"path": ".cursor/rules/executive-assistant.mdc",
"mediaType": "text/markdown",
- "content": "---\ndescription: \u0022Coordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\u0022\nglobs:\nalwaysApply: true\n---\n\n# Helio Executive Assistant\n\nCoordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Distinguish drafts from messages approved for delivery.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060context/organization.md\u0060\n- \u0060principal\u0060: \u0060context/principal.md\u0060\n- \u0060safetyPolicy\u0060: \u0060context/safety-policy.md\u0060\n\n## Available skills\n\n- \u0060executive-assistant.prepare-brief\u0060: Assemble an executive brief, requesting repository evidence when needed.\n- \u0060executive-assistant.triage-message\u0060: Classify an inbound message and recommend an accountable next action.\n\n"
+ "content": "---\ndescription: \u0022Coordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\u0022\nglobs:\nalwaysApply: true\n---\n\n# Helio Executive Assistant\n\nCoordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Distinguish drafts from messages approved for delivery.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060helio/context/organization@1.0.0\u0060\n- \u0060principal\u0060: \u0060helio/context/principal@1.0.0\u0060\n\n## Context\n\n### Executive Rhythm\n\n# Executive rhythm\n\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\n\n### Helio Works\n\n# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n\n### Principal\n\n# Principal\n\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\n\n### Helio Safety Policy\n\n# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n\n## Available skills\n\n- \u0060executive-assistant.prepare-brief\u0060: Assemble an executive brief, requesting repository evidence when needed.\n\nBuild a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, use the payments repository agent tool and\nincorporate its returned evidence with attribution.\n\n- \u0060executive-assistant.triage-message\u0060: Classify an inbound message and recommend an accountable next action.\n\nRead the message and identify its sender, intent, urgency, decision owner, and requested deadline.\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\n\n\n"
},
{
"path": ".typeference/bundle.json",
"mediaType": "application/json",
- "content": "{\n \u0022id\u0022: \u0022helio/executive-assistant@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Executive Assistant\u0022,\n \u0022description\u0022: \u0022Coordinates an executive\\u0027s correspondence, briefings, and cross-agent requests.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/person-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/correspondent@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022context/organization.md\u0022,\n \u0022principal\u0022: \u0022context/principal.md\u0022,\n \u0022safetyPolicy\u0022: \u0022context/safety-policy.md\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Distinguish drafts from messages approved for delivery.\u0022\n ],\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/principal.md\u0022,\n \u0022context/executive-rhythm.md\u0022\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.prepare-brief\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022,\n \u0022description\u0022: \u0022Assemble an executive brief, requesting repository evidence when needed.\u0022,\n \u0022instructions\u0022: \u0022Build a decision-oriented brief from the supplied topic and evidence.\\nWhen repository status is material, request \\u0060payments-repo-agent.repository-status\\u0060 and incorporate its returned evidence with attribution.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022topic\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022topic\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022brief\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022openQuestions\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022brief\\u0022,\\u0022openQuestions\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/principal.md\u0022,\n \u0022context/executive-rhythm.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022\n }\n ]\n },\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.triage-message\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/triage-message@1.0.0\u0022,\n \u0022description\u0022: \u0022Classify an inbound message and recommend an accountable next action.\u0022,\n \u0022instructions\u0022: \u0022Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.\\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022message\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022message\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022summary\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022urgency\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022owner\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022nextAction\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022summary\\u0022,\\u0022urgency\\u0022,\\u0022owner\\u0022,\\u0022nextAction\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/principal.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/triage-message@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.safetyPolicy\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.principal\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/person-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/correspondent@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n }\n ]\n}\n"
+ "content": "{\n \u0022id\u0022: \u0022helio/executive-assistant@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Executive Assistant\u0022,\n \u0022description\u0022: \u0022Coordinates an executive\\u0027s correspondence, briefings, and cross-agent requests.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/person-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/correspondent@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022principal\u0022: \u0022helio/context/principal@1.0.0\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Distinguish drafts from messages approved for delivery.\u0022\n ],\n \u0022context\u0022: [\n {\n \u0022id\u0022: \u0022helio/context/executive-rhythm@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/executive-rhythm@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/executive-rhythm@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Executive rhythm\\n\\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/organization@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/organization@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Helio Works\\n\\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/principal@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/principal@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/principal@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Principal\\n\\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/safety-policy@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/safety-policy@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/safety-policy@1.0.0\u0022\n ],\n \u0022values\u0022: {\n \u0022authority\u0022: \u0022explicit\u0022\n },\n \u0022content\u0022: \u0022# Safety policy\\n\\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\\n\u0022\n }\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.prepare-brief\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022,\n \u0022description\u0022: \u0022Assemble an executive brief, requesting repository evidence when needed.\u0022,\n \u0022instructions\u0022: \u0022Build a decision-oriented brief from the supplied topic and evidence.\\nWhen repository status is material, use the payments repository agent tool and\\nincorporate its returned evidence with attribution.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022topic\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022topic\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022brief\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022openQuestions\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022brief\\u0022,\\u0022openQuestions\\u0022]}\u0022,\n \u0022requiresContextTypes\u0022: [\n \u0022helio/context-types/executive-rhythm@1.0.0\u0022\n ],\n \u0022requiresTools\u0022: [\n \u0022helio/tools/payments-repository-agent@1.0.0\u0022\n ],\n \u0022exposed\u0022: true,\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/prepare-brief@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022\n }\n ]\n },\n {\n \u0022dispatchName\u0022: \u0022executive-assistant.triage-message\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/triage-message@1.0.0\u0022,\n \u0022description\u0022: \u0022Classify an inbound message and recommend an accountable next action.\u0022,\n \u0022instructions\u0022: \u0022Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.\\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022message\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022message\\u0022],\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022summary\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022urgency\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022owner\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022nextAction\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022required\\u0022:[\\u0022summary\\u0022,\\u0022urgency\\u0022,\\u0022owner\\u0022,\\u0022nextAction\\u0022]}\u0022,\n \u0022requiresContextTypes\u0022: [\n \u0022helio/context-types/safety-policy@1.0.0\u0022\n ],\n \u0022exposed\u0022: true,\n \u0022sealed\u0022: true,\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/triage-message@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/triage-message@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.principal\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/person-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/person-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/correspondent@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/executive-assistant@1.0.0\u0022\n }\n ]\n}\n"
+ },
+ {
+ "path": ".typeference/link.json",
+ "mediaType": "application/json",
+ "content": "{\n \u0022schemaVersion\u0022: 1,\n \u0022agentId\u0022: \u0022helio/executive-assistant@1.0.0\u0022,\n \u0022target\u0022: \u0022cursor\u0022,\n \u0022defaultMode\u0022: \u0022manual\u0022,\n \u0022sourceDigest\u0022: \u0022sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac\u0022,\n \u0022dependencies\u0022: [],\n \u0022modes\u0022: [],\n \u0022toolImports\u0022: [\n {\n \u0022toolId\u0022: \u0022helio/tools/payments-repository-agent@1.0.0\u0022,\n \u0022skillId\u0022: \u0022helio/skills/prepare-brief@1.0.0\u0022,\n \u0022mode\u0022: \u0022*\u0022\n }\n ]\n}\n"
},
{
"path": "AGENTS.md",
"mediaType": "text/markdown",
- "content": "# Helio Executive Assistant\n\nCoordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Distinguish drafts from messages approved for delivery.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060context/organization.md\u0060\n- \u0060principal\u0060: \u0060context/principal.md\u0060\n- \u0060safetyPolicy\u0060: \u0060context/safety-policy.md\u0060\n\n## Available skills\n\n- \u0060executive-assistant.prepare-brief\u0060: Assemble an executive brief, requesting repository evidence when needed.\n- \u0060executive-assistant.triage-message\u0060: Classify an inbound message and recommend an accountable next action.\n\n"
+ "content": "# Helio Executive Assistant\n\nCoordinates an executive\u0027s correspondence, briefings, and cross-agent requests.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Distinguish drafts from messages approved for delivery.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060helio/context/organization@1.0.0\u0060\n- \u0060principal\u0060: \u0060helio/context/principal@1.0.0\u0060\n\n## Context\n\n### Executive Rhythm\n\n# Executive rhythm\n\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\n\n### Helio Works\n\n# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n\n### Principal\n\n# Principal\n\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\n\n### Helio Safety Policy\n\n# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n\n## Available skills\n\n- \u0060executive-assistant.prepare-brief\u0060: Assemble an executive brief, requesting repository evidence when needed.\n\nBuild a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, use the payments repository agent tool and\nincorporate its returned evidence with attribution.\n\n- \u0060executive-assistant.triage-message\u0060: Classify an inbound message and recommend an accountable next action.\n\nRead the message and identify its sender, intent, urgency, decision owner, and requested deadline.\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\n\n\n"
}
]
},
"metadata": {
"generatedBy": "TypeFerence",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26",
- "sourceIdentifier": "urn:air:helio.example:typeference:source:helio",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "sourceIdentifier": "urn:air:helio.example:typeference:source:helio-agents",
"target": "cursor"
},
"trustManifest": {
@@ -493,8 +578,8 @@
"provenance": [
{
"relation": "derivedFrom",
- "sourceId": "urn:air:helio.example:typeference:source:helio",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26"
+ "sourceId": "urn:air:helio.example:typeference:source:helio-agents",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac"
}
]
}
@@ -516,24 +601,29 @@
{
"path": ".cursor/rules/payments-repo-agent.mdc",
"mediaType": "text/markdown",
- "content": "---\ndescription: \u0022Specializes repository assistance for the fictional payments service.\u0022\nglobs:\nalwaysApply: true\n---\n\n# Helio Payments Repository Agent\n\nSpecializes repository assistance for the fictional payments service.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Prefer evidence from tests, diffs, and version control over recollection.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060context/organization.md\u0060\n- \u0060repository\u0060: \u0060context/repository.md\u0060\n- \u0060safetyPolicy\u0060: \u0060context/safety-policy.md\u0060\n\n## Available skills\n\n- \u0060payments-repo-agent.repository-status\u0060: Report payments-service health with contract and reconciliation evidence.\n\n"
+ "content": "---\ndescription: \u0022Specializes repository assistance for the fictional payments service.\u0022\nglobs:\nalwaysApply: true\n---\n\n# Helio Payments Repository Agent\n\nSpecializes repository assistance for the fictional payments service.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Prefer evidence from tests, diffs, and version control over recollection.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060helio/context/organization@1.0.0\u0060\n- \u0060repository\u0060: \u0060helio/context/repository-evidence@1.0.0\u0060\n\n## Context\n\n### Helio Works\n\n# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n\n### Payments Service\n\n# Payments service\n\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\n\n### Repository Evidence\n\n# Repository evidence\n\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\n\n### Helio Safety Policy\n\n# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n\n## Available skills\n\n- \u0060payments-repo-agent.repository-status\u0060: Report payments-service health with contract and reconciliation evidence.\n\nApply the repository-status capability for the payments service, then include\npayment-contract compatibility, reconciliation checks, and rollback readiness,\nsurfacing uncertainty conversationally. Do not report the service healthy when\nany required financial-control signal is unavailable.\n\n\n"
},
{
"path": ".typeference/bundle.json",
"mediaType": "application/json",
- "content": "{\n \u0022id\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Payments Repository Agent\u0022,\n \u0022description\u0022: \u0022Specializes repository assistance for the fictional payments service.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/repository-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/repository-observer@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022context/organization.md\u0022,\n \u0022repository\u0022: \u0022context/repository.md\u0022,\n \u0022safetyPolicy\u0022: \u0022context/safety-policy.md\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Prefer evidence from tests, diffs, and version control over recollection.\u0022\n ],\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/repository.md\u0022,\n \u0022context/payments-service.md\u0022\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022payments-repo-agent.repository-status\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022,\n \u0022description\u0022: \u0022Report payments-service health with contract and reconciliation evidence.\u0022,\n \u0022instructions\u0022: \u0022Apply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.\\nDo not report the service healthy when any required financial-control signal is unavailable.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022focus\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022status\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022risks\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022status\\u0022,\\u0022evidence\\u0022,\\u0022risks\\u0022]}\u0022,\n \u0022contextFiles\u0022: [\n \u0022context/organization.md\u0022,\n \u0022context/safety-policy.md\u0022,\n \u0022context/repository.md\u0022,\n \u0022context/payments-service.md\u0022\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.safetyPolicy\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repository\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/repository-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022contextFiles\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/repository-observer@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n }\n ]\n}\n"
+ "content": "{\n \u0022id\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022,\n \u0022displayName\u0022: \u0022Helio Payments Repository Agent\u0022,\n \u0022description\u0022: \u0022Specializes repository assistance for the fictional payments service.\u0022,\n \u0022emit\u0022: true,\n \u0022embeds\u0022: [\n \u0022helio/profiles/repository-defaults@1.0.0\u0022\n ],\n \u0022satisfies\u0022: [\n \u0022helio/interfaces/repository-observer@1.0.0\u0022\n ],\n \u0022slots\u0022: {\n \u0022organization\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022repository\u0022: \u0022helio/context/repository-evidence@1.0.0\u0022\n },\n \u0022workingNorms\u0022: [\n \u0022Preserve a clear audit trail for material decisions.\u0022,\n \u0022State uncertainty and route work to an accountable owner when authority is unclear.\u0022,\n \u0022Prefer evidence from tests, diffs, and version control over recollection.\u0022\n ],\n \u0022context\u0022: [\n {\n \u0022id\u0022: \u0022helio/context/organization@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/organization@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/organization@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Helio Works\\n\\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/payments-service@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/payments-service@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/payments-service@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Payments service\\n\\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/repository-evidence@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/repository-evidence@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/repository-evidence@1.0.0\u0022\n ],\n \u0022content\u0022: \u0022# Repository evidence\\n\\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\\n\u0022\n },\n {\n \u0022id\u0022: \u0022helio/context/safety-policy@1.0.0\u0022,\n \u0022contextType\u0022: \u0022helio/context-types/safety-policy@1.0.0\u0022,\n \u0022satisfies\u0022: [\n \u0022helio/context-types/safety-policy@1.0.0\u0022\n ],\n \u0022values\u0022: {\n \u0022authority\u0022: \u0022explicit\u0022\n },\n \u0022content\u0022: \u0022# Safety policy\\n\\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\\n\u0022\n }\n ],\n \u0022skills\u0022: [\n {\n \u0022dispatchName\u0022: \u0022payments-repo-agent.repository-status\u0022,\n \u0022capabilityId\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022,\n \u0022implementationId\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022,\n \u0022description\u0022: \u0022Report payments-service health with contract and reconciliation evidence.\u0022,\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service and emit only\\nthe strict output object. Mark any unavailable financial-control signal as an\\nexplicit null; do not report the service healthy when one is missing.\\n\u0022,\n \u0022inputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022focus\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022additionalProperties\\u0022:false}\u0022,\n \u0022outputSchema\u0022: \u0022{\\u0022type\\u0022:\\u0022object\\u0022,\\u0022properties\\u0022:{\\u0022status\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022},\\u0022evidence\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}},\\u0022risks\\u0022:{\\u0022type\\u0022:\\u0022array\\u0022,\\u0022items\\u0022:{\\u0022type\\u0022:\\u0022string\\u0022}}},\\u0022required\\u0022:[\\u0022status\\u0022,\\u0022evidence\\u0022,\\u0022risks\\u0022]}\u0022,\n \u0022variants\u0022: {\n \u0022a2a\u0022: {\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service and return\\nattributed evidence for the calling agent \\u2014 contract compatibility,\\nreconciliation, and rollback readiness \\u2014 with no user-facing framing. Do not\\nreport the service healthy when any required financial-control signal is\\nunavailable.\\n\u0022,\n \u0022requiresContextTypes\u0022: [],\n \u0022requiresTools\u0022: []\n },\n \u0022manual\u0022: {\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service, then include\\npayment-contract compatibility, reconciliation checks, and rollback readiness,\\nsurfacing uncertainty conversationally. Do not report the service healthy when\\nany required financial-control signal is unavailable.\\n\u0022,\n \u0022requiresContextTypes\u0022: [],\n \u0022requiresTools\u0022: []\n },\n \u0022pipeline\u0022: {\n \u0022instructions\u0022: \u0022Apply the repository-status capability for the payments service and emit only\\nthe strict output object. Mark any unavailable financial-control signal as an\\nexplicit null; do not report the service healthy when one is missing.\\n\u0022,\n \u0022requiresContextTypes\u0022: [],\n \u0022requiresTools\u0022: []\n }\n },\n \u0022requiresContextTypes\u0022: [\n \u0022helio/context-types/payments-service@1.0.0\u0022\n ],\n \u0022requiresTools\u0022: [\n \u0022helio/tools/repository-signals@1.0.0\u0022\n ],\n \u0022exposed\u0022: true,\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022skill.capability\u0022,\n \u0022source\u0022: \u0022helio/capabilities/repository-status@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022skill.implementation\u0022,\n \u0022source\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022\n }\n ]\n }\n ],\n \u0022provenance\u0022: [\n {\n \u0022field\u0022: \u0022slots.organization\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/enterprise-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/enterprise-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022slots.repository\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022workingNorms\u0022,\n \u0022source\u0022: \u0022helio/profiles/repository-defaults@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022embeds.helio/profiles/repository-defaults@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022displayName\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022description\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n },\n {\n \u0022field\u0022: \u0022satisfies.helio/interfaces/repository-observer@1.0.0\u0022,\n \u0022source\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022\n }\n ]\n}\n"
+ },
+ {
+ "path": ".typeference/link.json",
+ "mediaType": "application/json",
+ "content": "{\n \u0022schemaVersion\u0022: 1,\n \u0022agentId\u0022: \u0022helio/payments-repo-agent@1.0.0\u0022,\n \u0022target\u0022: \u0022cursor\u0022,\n \u0022defaultMode\u0022: \u0022manual\u0022,\n \u0022sourceDigest\u0022: \u0022sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac\u0022,\n \u0022dependencies\u0022: [],\n \u0022modes\u0022: [\n \u0022a2a\u0022,\n \u0022manual\u0022,\n \u0022pipeline\u0022\n ],\n \u0022toolImports\u0022: [\n {\n \u0022toolId\u0022: \u0022helio/tools/repository-signals@1.0.0\u0022,\n \u0022skillId\u0022: \u0022helio/skills/payments-repository-status@1.0.0\u0022,\n \u0022mode\u0022: \u0022*\u0022\n }\n ]\n}\n"
},
{
"path": "AGENTS.md",
"mediaType": "text/markdown",
- "content": "# Helio Payments Repository Agent\n\nSpecializes repository assistance for the fictional payments service.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Prefer evidence from tests, diffs, and version control over recollection.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060context/organization.md\u0060\n- \u0060repository\u0060: \u0060context/repository.md\u0060\n- \u0060safetyPolicy\u0060: \u0060context/safety-policy.md\u0060\n\n## Available skills\n\n- \u0060payments-repo-agent.repository-status\u0060: Report payments-service health with contract and reconciliation evidence.\n\n"
+ "content": "# Helio Payments Repository Agent\n\nSpecializes repository assistance for the fictional payments service.\n\n## Working norms\n\n- Preserve a clear audit trail for material decisions.\n- State uncertainty and route work to an accountable owner when authority is unclear.\n- Prefer evidence from tests, diffs, and version control over recollection.\n\n## Context slots\n\n- \u0060organization\u0060: \u0060helio/context/organization@1.0.0\u0060\n- \u0060repository\u0060: \u0060helio/context/repository-evidence@1.0.0\u0060\n\n## Context\n\n### Helio Works\n\n# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n\n### Payments Service\n\n# Payments service\n\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\n\n### Repository Evidence\n\n# Repository evidence\n\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\n\n### Helio Safety Policy\n\n# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n\n## Available skills\n\n- \u0060payments-repo-agent.repository-status\u0060: Report payments-service health with contract and reconciliation evidence.\n\nApply the repository-status capability for the payments service, then include\npayment-contract compatibility, reconciliation checks, and rollback readiness,\nsurfacing uncertainty conversationally. Do not report the service healthy when\nany required financial-control signal is unavailable.\n\n\n"
}
]
},
"metadata": {
"generatedBy": "TypeFerence",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26",
- "sourceIdentifier": "urn:air:helio.example:typeference:source:helio",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "sourceIdentifier": "urn:air:helio.example:typeference:source:helio-agents",
"target": "cursor"
},
"trustManifest": {
@@ -542,8 +632,8 @@
"provenance": [
{
"relation": "derivedFrom",
- "sourceId": "urn:air:helio.example:typeference:source:helio",
- "sourceDigest": "sha256:70270ce0b55dd8c489246a0cffcd64b76f87b4f99e2815ce471ffc7c7dc79d26"
+ "sourceId": "urn:air:helio.example:typeference:source:helio-agents",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac"
}
]
}
diff --git a/dist/codex/.typeference/build.json b/dist/codex/.typeference/build.json
new file mode 100644
index 0000000..5c5933f
--- /dev/null
+++ b/dist/codex/.typeference/build.json
@@ -0,0 +1,17 @@
+{
+ "schemaVersion": 1,
+ "target": "codex",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "artifacts": [
+ {
+ "agentId": "helio/executive-assistant@1.0.0",
+ "path": "executive-assistant",
+ "digest": "sha256:02d28d1dc4ceec13d25c1d4a32229d9ff956c0586fdbd123a41c3b8a83c86c87"
+ },
+ {
+ "agentId": "helio/payments-repo-agent@1.0.0",
+ "path": "payments-repo-agent",
+ "digest": "sha256:131be31e4d0839890ead6a31de53a96ba796f30eab1a9f610154c1b84eff0722"
+ }
+ ]
+}
diff --git a/dist/codex/executive-assistant/.agents/skills/prepare-brief/SKILL.md b/dist/codex/executive-assistant/.agents/skills/prepare-brief/SKILL.md
index 0b2f4b4..de9d46a 100644
--- a/dist/codex/executive-assistant/.agents/skills/prepare-brief/SKILL.md
+++ b/dist/codex/executive-assistant/.agents/skills/prepare-brief/SKILL.md
@@ -4,11 +4,5 @@ description: "Assemble an executive brief, requesting repository evidence when n
---
Build a decision-oriented brief from the supplied topic and evidence.
-When repository status is material, request `payments-repo-agent.repository-status` and incorporate its returned evidence with attribution.
-
-## Context loaded on invocation
-
-- `context/organization.md`
-- `context/safety-policy.md`
-- `context/principal.md`
-- `context/executive-rhythm.md`
+When repository status is material, use the payments repository agent tool and
+incorporate its returned evidence with attribution.
diff --git a/dist/codex/executive-assistant/.agents/skills/triage-message/SKILL.md b/dist/codex/executive-assistant/.agents/skills/triage-message/SKILL.md
index c349483..c2fcd1f 100644
--- a/dist/codex/executive-assistant/.agents/skills/triage-message/SKILL.md
+++ b/dist/codex/executive-assistant/.agents/skills/triage-message/SKILL.md
@@ -5,9 +5,3 @@ description: "Classify an inbound message and recommend an accountable next acti
Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.
Separate facts from assumptions. Return a concise recommendation; do not send a reply.
-
-## Context loaded on invocation
-
-- `context/organization.md`
-- `context/safety-policy.md`
-- `context/principal.md`
diff --git a/dist/codex/executive-assistant/.codex/config.toml b/dist/codex/executive-assistant/.codex/config.toml
deleted file mode 100644
index ef8dda8..0000000
--- a/dist/codex/executive-assistant/.codex/config.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-[mcp_servers.typeference]
-command = "typeference"
-args = ["serve", ".typeference"]
diff --git a/dist/codex/executive-assistant/.typeference/bundle.json b/dist/codex/executive-assistant/.typeference/bundle.json
index c343d10..89c9add 100644
--- a/dist/codex/executive-assistant/.typeference/bundle.json
+++ b/dist/codex/executive-assistant/.typeference/bundle.json
@@ -10,20 +10,50 @@
"helio/interfaces/correspondent@1.0.0"
],
"slots": {
- "organization": "context/organization.md",
- "principal": "context/principal.md",
- "safetyPolicy": "context/safety-policy.md"
+ "organization": "helio/context/organization@1.0.0",
+ "principal": "helio/context/principal@1.0.0"
},
"workingNorms": [
"Preserve a clear audit trail for material decisions.",
"State uncertainty and route work to an accountable owner when authority is unclear.",
"Distinguish drafts from messages approved for delivery."
],
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/principal.md",
- "context/executive-rhythm.md"
+ "context": [
+ {
+ "id": "helio/context/executive-rhythm@1.0.0",
+ "contextType": "helio/context-types/executive-rhythm@1.0.0",
+ "satisfies": [
+ "helio/context-types/executive-rhythm@1.0.0"
+ ],
+ "content": "# Executive rhythm\n\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\n"
+ },
+ {
+ "id": "helio/context/organization@1.0.0",
+ "contextType": "helio/context-types/organization@1.0.0",
+ "satisfies": [
+ "helio/context-types/organization@1.0.0"
+ ],
+ "content": "# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n"
+ },
+ {
+ "id": "helio/context/principal@1.0.0",
+ "contextType": "helio/context-types/principal@1.0.0",
+ "satisfies": [
+ "helio/context-types/principal@1.0.0"
+ ],
+ "content": "# Principal\n\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\n"
+ },
+ {
+ "id": "helio/context/safety-policy@1.0.0",
+ "contextType": "helio/context-types/safety-policy@1.0.0",
+ "satisfies": [
+ "helio/context-types/safety-policy@1.0.0"
+ ],
+ "values": {
+ "authority": "explicit"
+ },
+ "content": "# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n"
+ }
],
"skills": [
{
@@ -31,15 +61,16 @@
"capabilityId": "helio/capabilities/prepare-brief@1.0.0",
"implementationId": "helio/skills/prepare-brief@1.0.0",
"description": "Assemble an executive brief, requesting repository evidence when needed.",
- "instructions": "Build a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, request \u0060payments-repo-agent.repository-status\u0060 and incorporate its returned evidence with attribution.\n",
+ "instructions": "Build a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, use the payments repository agent tool and\nincorporate its returned evidence with attribution.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022topic\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022topic\u0022],\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022brief\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022openQuestions\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022brief\u0022,\u0022openQuestions\u0022]}",
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/principal.md",
- "context/executive-rhythm.md"
+ "requiresContextTypes": [
+ "helio/context-types/executive-rhythm@1.0.0"
+ ],
+ "requiresTools": [
+ "helio/tools/payments-repository-agent@1.0.0"
],
+ "exposed": true,
"provenance": [
{
"field": "skill.capability",
@@ -59,11 +90,11 @@
"instructions": "Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022message\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022message\u0022],\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022summary\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022urgency\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022owner\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022nextAction\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022summary\u0022,\u0022urgency\u0022,\u0022owner\u0022,\u0022nextAction\u0022]}",
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/principal.md"
+ "requiresContextTypes": [
+ "helio/context-types/safety-policy@1.0.0"
],
+ "exposed": true,
+ "sealed": true,
"provenance": [
{
"field": "skill.capability",
@@ -81,10 +112,6 @@
"field": "slots.organization",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "slots.safetyPolicy",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
@@ -93,14 +120,6 @@
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/enterprise-defaults@1.0.0",
"source": "helio/profiles/person-defaults@1.0.0"
@@ -113,10 +132,6 @@
"field": "workingNorms",
"source": "helio/profiles/person-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/person-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/person-defaults@1.0.0",
"source": "helio/executive-assistant@1.0.0"
@@ -129,10 +144,6 @@
"field": "description",
"source": "helio/executive-assistant@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/executive-assistant@1.0.0"
- },
{
"field": "satisfies.helio/interfaces/correspondent@1.0.0",
"source": "helio/executive-assistant@1.0.0"
diff --git a/dist/codex/executive-assistant/.typeference/link.json b/dist/codex/executive-assistant/.typeference/link.json
new file mode 100644
index 0000000..7024fad
--- /dev/null
+++ b/dist/codex/executive-assistant/.typeference/link.json
@@ -0,0 +1,16 @@
+{
+ "schemaVersion": 1,
+ "agentId": "helio/executive-assistant@1.0.0",
+ "target": "codex",
+ "defaultMode": "manual",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "dependencies": [],
+ "modes": [],
+ "toolImports": [
+ {
+ "toolId": "helio/tools/payments-repository-agent@1.0.0",
+ "skillId": "helio/skills/prepare-brief@1.0.0",
+ "mode": "*"
+ }
+ ]
+}
diff --git a/dist/codex/executive-assistant/AGENTS.md b/dist/codex/executive-assistant/AGENTS.md
index 8af3450..2e69ac5 100644
--- a/dist/codex/executive-assistant/AGENTS.md
+++ b/dist/codex/executive-assistant/AGENTS.md
@@ -10,9 +10,34 @@ Coordinates an executive's correspondence, briefings, and cross-agent requests.
## Context slots
-- `organization`: `context/organization.md`
-- `principal`: `context/principal.md`
-- `safetyPolicy`: `context/safety-policy.md`
+- `organization`: `helio/context/organization@1.0.0`
+- `principal`: `helio/context/principal@1.0.0`
+
+## Context
+
+### Executive Rhythm
+
+# Executive rhythm
+
+Daily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.
+
+### Helio Works
+
+# Helio Works
+
+Helio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.
+
+### Principal
+
+# Principal
+
+The principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.
+
+### Helio Safety Policy
+
+# Safety policy
+
+Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
## Available skills
diff --git a/dist/codex/payments-repo-agent/.agents/skills/repository-status/SKILL.md b/dist/codex/payments-repo-agent/.agents/skills/repository-status/SKILL.md
index 9e8fcf8..b0c22ea 100644
--- a/dist/codex/payments-repo-agent/.agents/skills/repository-status/SKILL.md
+++ b/dist/codex/payments-repo-agent/.agents/skills/repository-status/SKILL.md
@@ -3,12 +3,7 @@ name: repository-status
description: "Report payments-service health with contract and reconciliation evidence."
---
-Apply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.
-Do not report the service healthy when any required financial-control signal is unavailable.
-
-## Context loaded on invocation
-
-- `context/organization.md`
-- `context/safety-policy.md`
-- `context/repository.md`
-- `context/payments-service.md`
+Apply the repository-status capability for the payments service, then include
+payment-contract compatibility, reconciliation checks, and rollback readiness,
+surfacing uncertainty conversationally. Do not report the service healthy when
+any required financial-control signal is unavailable.
diff --git a/dist/codex/payments-repo-agent/.codex/config.toml b/dist/codex/payments-repo-agent/.codex/config.toml
deleted file mode 100644
index ef8dda8..0000000
--- a/dist/codex/payments-repo-agent/.codex/config.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-[mcp_servers.typeference]
-command = "typeference"
-args = ["serve", ".typeference"]
diff --git a/dist/codex/payments-repo-agent/.typeference/bundle.json b/dist/codex/payments-repo-agent/.typeference/bundle.json
index 1396aa0..e153f7a 100644
--- a/dist/codex/payments-repo-agent/.typeference/bundle.json
+++ b/dist/codex/payments-repo-agent/.typeference/bundle.json
@@ -10,20 +10,50 @@
"helio/interfaces/repository-observer@1.0.0"
],
"slots": {
- "organization": "context/organization.md",
- "repository": "context/repository.md",
- "safetyPolicy": "context/safety-policy.md"
+ "organization": "helio/context/organization@1.0.0",
+ "repository": "helio/context/repository-evidence@1.0.0"
},
"workingNorms": [
"Preserve a clear audit trail for material decisions.",
"State uncertainty and route work to an accountable owner when authority is unclear.",
"Prefer evidence from tests, diffs, and version control over recollection."
],
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/repository.md",
- "context/payments-service.md"
+ "context": [
+ {
+ "id": "helio/context/organization@1.0.0",
+ "contextType": "helio/context-types/organization@1.0.0",
+ "satisfies": [
+ "helio/context-types/organization@1.0.0"
+ ],
+ "content": "# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n"
+ },
+ {
+ "id": "helio/context/payments-service@1.0.0",
+ "contextType": "helio/context-types/payments-service@1.0.0",
+ "satisfies": [
+ "helio/context-types/payments-service@1.0.0"
+ ],
+ "content": "# Payments service\n\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\n"
+ },
+ {
+ "id": "helio/context/repository-evidence@1.0.0",
+ "contextType": "helio/context-types/repository-evidence@1.0.0",
+ "satisfies": [
+ "helio/context-types/repository-evidence@1.0.0"
+ ],
+ "content": "# Repository evidence\n\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\n"
+ },
+ {
+ "id": "helio/context/safety-policy@1.0.0",
+ "contextType": "helio/context-types/safety-policy@1.0.0",
+ "satisfies": [
+ "helio/context-types/safety-policy@1.0.0"
+ ],
+ "values": {
+ "authority": "explicit"
+ },
+ "content": "# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n"
+ }
],
"skills": [
{
@@ -31,15 +61,33 @@
"capabilityId": "helio/capabilities/repository-status@1.0.0",
"implementationId": "helio/skills/payments-repository-status@1.0.0",
"description": "Report payments-service health with contract and reconciliation evidence.",
- "instructions": "Apply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.\nDo not report the service healthy when any required financial-control signal is unavailable.\n",
+ "instructions": "Apply the repository-status capability for the payments service and emit only\nthe strict output object. Mark any unavailable financial-control signal as an\nexplicit null; do not report the service healthy when one is missing.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022focus\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022status\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022risks\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022status\u0022,\u0022evidence\u0022,\u0022risks\u0022]}",
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/repository.md",
- "context/payments-service.md"
+ "variants": {
+ "a2a": {
+ "instructions": "Apply the repository-status capability for the payments service and return\nattributed evidence for the calling agent \u2014 contract compatibility,\nreconciliation, and rollback readiness \u2014 with no user-facing framing. Do not\nreport the service healthy when any required financial-control signal is\nunavailable.\n",
+ "requiresContextTypes": [],
+ "requiresTools": []
+ },
+ "manual": {
+ "instructions": "Apply the repository-status capability for the payments service, then include\npayment-contract compatibility, reconciliation checks, and rollback readiness,\nsurfacing uncertainty conversationally. Do not report the service healthy when\nany required financial-control signal is unavailable.\n",
+ "requiresContextTypes": [],
+ "requiresTools": []
+ },
+ "pipeline": {
+ "instructions": "Apply the repository-status capability for the payments service and emit only\nthe strict output object. Mark any unavailable financial-control signal as an\nexplicit null; do not report the service healthy when one is missing.\n",
+ "requiresContextTypes": [],
+ "requiresTools": []
+ }
+ },
+ "requiresContextTypes": [
+ "helio/context-types/payments-service@1.0.0"
+ ],
+ "requiresTools": [
+ "helio/tools/repository-signals@1.0.0"
],
+ "exposed": true,
"provenance": [
{
"field": "skill.capability",
@@ -57,10 +105,6 @@
"field": "slots.organization",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "slots.safetyPolicy",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
@@ -69,14 +113,6 @@
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/enterprise-defaults@1.0.0",
"source": "helio/profiles/repository-defaults@1.0.0"
@@ -89,10 +125,6 @@
"field": "workingNorms",
"source": "helio/profiles/repository-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/repository-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/repository-defaults@1.0.0",
"source": "helio/payments-repo-agent@1.0.0"
@@ -105,10 +137,6 @@
"field": "description",
"source": "helio/payments-repo-agent@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/payments-repo-agent@1.0.0"
- },
{
"field": "satisfies.helio/interfaces/repository-observer@1.0.0",
"source": "helio/payments-repo-agent@1.0.0"
diff --git a/dist/codex/payments-repo-agent/.typeference/link.json b/dist/codex/payments-repo-agent/.typeference/link.json
new file mode 100644
index 0000000..eb01481
--- /dev/null
+++ b/dist/codex/payments-repo-agent/.typeference/link.json
@@ -0,0 +1,20 @@
+{
+ "schemaVersion": 1,
+ "agentId": "helio/payments-repo-agent@1.0.0",
+ "target": "codex",
+ "defaultMode": "manual",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "dependencies": [],
+ "modes": [
+ "a2a",
+ "manual",
+ "pipeline"
+ ],
+ "toolImports": [
+ {
+ "toolId": "helio/tools/repository-signals@1.0.0",
+ "skillId": "helio/skills/payments-repository-status@1.0.0",
+ "mode": "*"
+ }
+ ]
+}
diff --git a/dist/codex/payments-repo-agent/AGENTS.md b/dist/codex/payments-repo-agent/AGENTS.md
index 915fa0a..a514905 100644
--- a/dist/codex/payments-repo-agent/AGENTS.md
+++ b/dist/codex/payments-repo-agent/AGENTS.md
@@ -10,9 +10,34 @@ Specializes repository assistance for the fictional payments service.
## Context slots
-- `organization`: `context/organization.md`
-- `repository`: `context/repository.md`
-- `safetyPolicy`: `context/safety-policy.md`
+- `organization`: `helio/context/organization@1.0.0`
+- `repository`: `helio/context/repository-evidence@1.0.0`
+
+## Context
+
+### Helio Works
+
+# Helio Works
+
+Helio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.
+
+### Payments Service
+
+# Payments service
+
+This fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.
+
+### Repository Evidence
+
+# Repository evidence
+
+Repository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.
+
+### Helio Safety Policy
+
+# Safety policy
+
+Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
## Available skills
diff --git a/dist/copilot/.typeference/build.json b/dist/copilot/.typeference/build.json
new file mode 100644
index 0000000..d78392a
--- /dev/null
+++ b/dist/copilot/.typeference/build.json
@@ -0,0 +1,17 @@
+{
+ "schemaVersion": 1,
+ "target": "copilot",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "artifacts": [
+ {
+ "agentId": "helio/executive-assistant@1.0.0",
+ "path": "executive-assistant",
+ "digest": "sha256:59dd5ce9cd7683b7384f775919dcdac8068953581e65661bcb40e52dab01d6c7"
+ },
+ {
+ "agentId": "helio/payments-repo-agent@1.0.0",
+ "path": "payments-repo-agent",
+ "digest": "sha256:f19db443327fe4fb509a0b854581635f33adc88589bba9bed2ceb476fdf651e6"
+ }
+ ]
+}
diff --git a/dist/copilot/executive-assistant/.github/agents/executive-assistant.agent.md b/dist/copilot/executive-assistant/.github/agents/executive-assistant.agent.md
index 6e587d3..1f9195b 100644
--- a/dist/copilot/executive-assistant/.github/agents/executive-assistant.agent.md
+++ b/dist/copilot/executive-assistant/.github/agents/executive-assistant.agent.md
@@ -15,12 +15,46 @@ Coordinates an executive's correspondence, briefings, and cross-agent requests.
## Context slots
-- `organization`: `context/organization.md`
-- `principal`: `context/principal.md`
-- `safetyPolicy`: `context/safety-policy.md`
+- `organization`: `helio/context/organization@1.0.0`
+- `principal`: `helio/context/principal@1.0.0`
+
+## Context
+
+### Executive Rhythm
+
+# Executive rhythm
+
+Daily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.
+
+### Helio Works
+
+# Helio Works
+
+Helio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.
+
+### Principal
+
+# Principal
+
+The principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.
+
+### Helio Safety Policy
+
+# Safety policy
+
+Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
## Available skills
- `executive-assistant.prepare-brief`: Assemble an executive brief, requesting repository evidence when needed.
+
+Build a decision-oriented brief from the supplied topic and evidence.
+When repository status is material, use the payments repository agent tool and
+incorporate its returned evidence with attribution.
+
- `executive-assistant.triage-message`: Classify an inbound message and recommend an accountable next action.
+Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.
+Separate facts from assumptions. Return a concise recommendation; do not send a reply.
+
+
diff --git a/dist/copilot/executive-assistant/.github/copilot-instructions.md b/dist/copilot/executive-assistant/.github/copilot-instructions.md
index 8af3450..6f8c935 100644
--- a/dist/copilot/executive-assistant/.github/copilot-instructions.md
+++ b/dist/copilot/executive-assistant/.github/copilot-instructions.md
@@ -10,12 +10,46 @@ Coordinates an executive's correspondence, briefings, and cross-agent requests.
## Context slots
-- `organization`: `context/organization.md`
-- `principal`: `context/principal.md`
-- `safetyPolicy`: `context/safety-policy.md`
+- `organization`: `helio/context/organization@1.0.0`
+- `principal`: `helio/context/principal@1.0.0`
+
+## Context
+
+### Executive Rhythm
+
+# Executive rhythm
+
+Daily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.
+
+### Helio Works
+
+# Helio Works
+
+Helio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.
+
+### Principal
+
+# Principal
+
+The principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.
+
+### Helio Safety Policy
+
+# Safety policy
+
+Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
## Available skills
- `executive-assistant.prepare-brief`: Assemble an executive brief, requesting repository evidence when needed.
+
+Build a decision-oriented brief from the supplied topic and evidence.
+When repository status is material, use the payments repository agent tool and
+incorporate its returned evidence with attribution.
+
- `executive-assistant.triage-message`: Classify an inbound message and recommend an accountable next action.
+Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.
+Separate facts from assumptions. Return a concise recommendation; do not send a reply.
+
+
diff --git a/dist/copilot/executive-assistant/.typeference/bundle.json b/dist/copilot/executive-assistant/.typeference/bundle.json
index c343d10..89c9add 100644
--- a/dist/copilot/executive-assistant/.typeference/bundle.json
+++ b/dist/copilot/executive-assistant/.typeference/bundle.json
@@ -10,20 +10,50 @@
"helio/interfaces/correspondent@1.0.0"
],
"slots": {
- "organization": "context/organization.md",
- "principal": "context/principal.md",
- "safetyPolicy": "context/safety-policy.md"
+ "organization": "helio/context/organization@1.0.0",
+ "principal": "helio/context/principal@1.0.0"
},
"workingNorms": [
"Preserve a clear audit trail for material decisions.",
"State uncertainty and route work to an accountable owner when authority is unclear.",
"Distinguish drafts from messages approved for delivery."
],
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/principal.md",
- "context/executive-rhythm.md"
+ "context": [
+ {
+ "id": "helio/context/executive-rhythm@1.0.0",
+ "contextType": "helio/context-types/executive-rhythm@1.0.0",
+ "satisfies": [
+ "helio/context-types/executive-rhythm@1.0.0"
+ ],
+ "content": "# Executive rhythm\n\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\n"
+ },
+ {
+ "id": "helio/context/organization@1.0.0",
+ "contextType": "helio/context-types/organization@1.0.0",
+ "satisfies": [
+ "helio/context-types/organization@1.0.0"
+ ],
+ "content": "# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n"
+ },
+ {
+ "id": "helio/context/principal@1.0.0",
+ "contextType": "helio/context-types/principal@1.0.0",
+ "satisfies": [
+ "helio/context-types/principal@1.0.0"
+ ],
+ "content": "# Principal\n\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\n"
+ },
+ {
+ "id": "helio/context/safety-policy@1.0.0",
+ "contextType": "helio/context-types/safety-policy@1.0.0",
+ "satisfies": [
+ "helio/context-types/safety-policy@1.0.0"
+ ],
+ "values": {
+ "authority": "explicit"
+ },
+ "content": "# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n"
+ }
],
"skills": [
{
@@ -31,15 +61,16 @@
"capabilityId": "helio/capabilities/prepare-brief@1.0.0",
"implementationId": "helio/skills/prepare-brief@1.0.0",
"description": "Assemble an executive brief, requesting repository evidence when needed.",
- "instructions": "Build a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, request \u0060payments-repo-agent.repository-status\u0060 and incorporate its returned evidence with attribution.\n",
+ "instructions": "Build a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, use the payments repository agent tool and\nincorporate its returned evidence with attribution.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022topic\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022topic\u0022],\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022brief\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022openQuestions\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022brief\u0022,\u0022openQuestions\u0022]}",
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/principal.md",
- "context/executive-rhythm.md"
+ "requiresContextTypes": [
+ "helio/context-types/executive-rhythm@1.0.0"
+ ],
+ "requiresTools": [
+ "helio/tools/payments-repository-agent@1.0.0"
],
+ "exposed": true,
"provenance": [
{
"field": "skill.capability",
@@ -59,11 +90,11 @@
"instructions": "Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022message\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022message\u0022],\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022summary\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022urgency\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022owner\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022nextAction\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022summary\u0022,\u0022urgency\u0022,\u0022owner\u0022,\u0022nextAction\u0022]}",
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/principal.md"
+ "requiresContextTypes": [
+ "helio/context-types/safety-policy@1.0.0"
],
+ "exposed": true,
+ "sealed": true,
"provenance": [
{
"field": "skill.capability",
@@ -81,10 +112,6 @@
"field": "slots.organization",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "slots.safetyPolicy",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
@@ -93,14 +120,6 @@
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/enterprise-defaults@1.0.0",
"source": "helio/profiles/person-defaults@1.0.0"
@@ -113,10 +132,6 @@
"field": "workingNorms",
"source": "helio/profiles/person-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/person-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/person-defaults@1.0.0",
"source": "helio/executive-assistant@1.0.0"
@@ -129,10 +144,6 @@
"field": "description",
"source": "helio/executive-assistant@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/executive-assistant@1.0.0"
- },
{
"field": "satisfies.helio/interfaces/correspondent@1.0.0",
"source": "helio/executive-assistant@1.0.0"
diff --git a/dist/copilot/executive-assistant/.typeference/link.json b/dist/copilot/executive-assistant/.typeference/link.json
new file mode 100644
index 0000000..e45d4ac
--- /dev/null
+++ b/dist/copilot/executive-assistant/.typeference/link.json
@@ -0,0 +1,16 @@
+{
+ "schemaVersion": 1,
+ "agentId": "helio/executive-assistant@1.0.0",
+ "target": "copilot",
+ "defaultMode": "manual",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "dependencies": [],
+ "modes": [],
+ "toolImports": [
+ {
+ "toolId": "helio/tools/payments-repository-agent@1.0.0",
+ "skillId": "helio/skills/prepare-brief@1.0.0",
+ "mode": "*"
+ }
+ ]
+}
diff --git a/dist/copilot/payments-repo-agent/.github/agents/payments-repo-agent.agent.md b/dist/copilot/payments-repo-agent/.github/agents/payments-repo-agent.agent.md
index 8801a5c..d1ec27b 100644
--- a/dist/copilot/payments-repo-agent/.github/agents/payments-repo-agent.agent.md
+++ b/dist/copilot/payments-repo-agent/.github/agents/payments-repo-agent.agent.md
@@ -15,11 +15,42 @@ Specializes repository assistance for the fictional payments service.
## Context slots
-- `organization`: `context/organization.md`
-- `repository`: `context/repository.md`
-- `safetyPolicy`: `context/safety-policy.md`
+- `organization`: `helio/context/organization@1.0.0`
+- `repository`: `helio/context/repository-evidence@1.0.0`
+
+## Context
+
+### Helio Works
+
+# Helio Works
+
+Helio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.
+
+### Payments Service
+
+# Payments service
+
+This fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.
+
+### Repository Evidence
+
+# Repository evidence
+
+Repository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.
+
+### Helio Safety Policy
+
+# Safety policy
+
+Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
## Available skills
- `payments-repo-agent.repository-status`: Report payments-service health with contract and reconciliation evidence.
+Apply the repository-status capability for the payments service, then include
+payment-contract compatibility, reconciliation checks, and rollback readiness,
+surfacing uncertainty conversationally. Do not report the service healthy when
+any required financial-control signal is unavailable.
+
+
diff --git a/dist/copilot/payments-repo-agent/.github/copilot-instructions.md b/dist/copilot/payments-repo-agent/.github/copilot-instructions.md
index 915fa0a..7a318fa 100644
--- a/dist/copilot/payments-repo-agent/.github/copilot-instructions.md
+++ b/dist/copilot/payments-repo-agent/.github/copilot-instructions.md
@@ -10,11 +10,42 @@ Specializes repository assistance for the fictional payments service.
## Context slots
-- `organization`: `context/organization.md`
-- `repository`: `context/repository.md`
-- `safetyPolicy`: `context/safety-policy.md`
+- `organization`: `helio/context/organization@1.0.0`
+- `repository`: `helio/context/repository-evidence@1.0.0`
+
+## Context
+
+### Helio Works
+
+# Helio Works
+
+Helio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.
+
+### Payments Service
+
+# Payments service
+
+This fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.
+
+### Repository Evidence
+
+# Repository evidence
+
+Repository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.
+
+### Helio Safety Policy
+
+# Safety policy
+
+Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
## Available skills
- `payments-repo-agent.repository-status`: Report payments-service health with contract and reconciliation evidence.
+Apply the repository-status capability for the payments service, then include
+payment-contract compatibility, reconciliation checks, and rollback readiness,
+surfacing uncertainty conversationally. Do not report the service healthy when
+any required financial-control signal is unavailable.
+
+
diff --git a/dist/copilot/payments-repo-agent/.typeference/bundle.json b/dist/copilot/payments-repo-agent/.typeference/bundle.json
index 1396aa0..e153f7a 100644
--- a/dist/copilot/payments-repo-agent/.typeference/bundle.json
+++ b/dist/copilot/payments-repo-agent/.typeference/bundle.json
@@ -10,20 +10,50 @@
"helio/interfaces/repository-observer@1.0.0"
],
"slots": {
- "organization": "context/organization.md",
- "repository": "context/repository.md",
- "safetyPolicy": "context/safety-policy.md"
+ "organization": "helio/context/organization@1.0.0",
+ "repository": "helio/context/repository-evidence@1.0.0"
},
"workingNorms": [
"Preserve a clear audit trail for material decisions.",
"State uncertainty and route work to an accountable owner when authority is unclear.",
"Prefer evidence from tests, diffs, and version control over recollection."
],
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/repository.md",
- "context/payments-service.md"
+ "context": [
+ {
+ "id": "helio/context/organization@1.0.0",
+ "contextType": "helio/context-types/organization@1.0.0",
+ "satisfies": [
+ "helio/context-types/organization@1.0.0"
+ ],
+ "content": "# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n"
+ },
+ {
+ "id": "helio/context/payments-service@1.0.0",
+ "contextType": "helio/context-types/payments-service@1.0.0",
+ "satisfies": [
+ "helio/context-types/payments-service@1.0.0"
+ ],
+ "content": "# Payments service\n\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\n"
+ },
+ {
+ "id": "helio/context/repository-evidence@1.0.0",
+ "contextType": "helio/context-types/repository-evidence@1.0.0",
+ "satisfies": [
+ "helio/context-types/repository-evidence@1.0.0"
+ ],
+ "content": "# Repository evidence\n\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\n"
+ },
+ {
+ "id": "helio/context/safety-policy@1.0.0",
+ "contextType": "helio/context-types/safety-policy@1.0.0",
+ "satisfies": [
+ "helio/context-types/safety-policy@1.0.0"
+ ],
+ "values": {
+ "authority": "explicit"
+ },
+ "content": "# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n"
+ }
],
"skills": [
{
@@ -31,15 +61,33 @@
"capabilityId": "helio/capabilities/repository-status@1.0.0",
"implementationId": "helio/skills/payments-repository-status@1.0.0",
"description": "Report payments-service health with contract and reconciliation evidence.",
- "instructions": "Apply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.\nDo not report the service healthy when any required financial-control signal is unavailable.\n",
+ "instructions": "Apply the repository-status capability for the payments service and emit only\nthe strict output object. Mark any unavailable financial-control signal as an\nexplicit null; do not report the service healthy when one is missing.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022focus\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022status\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022risks\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022status\u0022,\u0022evidence\u0022,\u0022risks\u0022]}",
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/repository.md",
- "context/payments-service.md"
+ "variants": {
+ "a2a": {
+ "instructions": "Apply the repository-status capability for the payments service and return\nattributed evidence for the calling agent \u2014 contract compatibility,\nreconciliation, and rollback readiness \u2014 with no user-facing framing. Do not\nreport the service healthy when any required financial-control signal is\nunavailable.\n",
+ "requiresContextTypes": [],
+ "requiresTools": []
+ },
+ "manual": {
+ "instructions": "Apply the repository-status capability for the payments service, then include\npayment-contract compatibility, reconciliation checks, and rollback readiness,\nsurfacing uncertainty conversationally. Do not report the service healthy when\nany required financial-control signal is unavailable.\n",
+ "requiresContextTypes": [],
+ "requiresTools": []
+ },
+ "pipeline": {
+ "instructions": "Apply the repository-status capability for the payments service and emit only\nthe strict output object. Mark any unavailable financial-control signal as an\nexplicit null; do not report the service healthy when one is missing.\n",
+ "requiresContextTypes": [],
+ "requiresTools": []
+ }
+ },
+ "requiresContextTypes": [
+ "helio/context-types/payments-service@1.0.0"
+ ],
+ "requiresTools": [
+ "helio/tools/repository-signals@1.0.0"
],
+ "exposed": true,
"provenance": [
{
"field": "skill.capability",
@@ -57,10 +105,6 @@
"field": "slots.organization",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "slots.safetyPolicy",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
@@ -69,14 +113,6 @@
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/enterprise-defaults@1.0.0",
"source": "helio/profiles/repository-defaults@1.0.0"
@@ -89,10 +125,6 @@
"field": "workingNorms",
"source": "helio/profiles/repository-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/repository-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/repository-defaults@1.0.0",
"source": "helio/payments-repo-agent@1.0.0"
@@ -105,10 +137,6 @@
"field": "description",
"source": "helio/payments-repo-agent@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/payments-repo-agent@1.0.0"
- },
{
"field": "satisfies.helio/interfaces/repository-observer@1.0.0",
"source": "helio/payments-repo-agent@1.0.0"
diff --git a/dist/copilot/payments-repo-agent/.typeference/link.json b/dist/copilot/payments-repo-agent/.typeference/link.json
new file mode 100644
index 0000000..85154c3
--- /dev/null
+++ b/dist/copilot/payments-repo-agent/.typeference/link.json
@@ -0,0 +1,20 @@
+{
+ "schemaVersion": 1,
+ "agentId": "helio/payments-repo-agent@1.0.0",
+ "target": "copilot",
+ "defaultMode": "manual",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "dependencies": [],
+ "modes": [
+ "a2a",
+ "manual",
+ "pipeline"
+ ],
+ "toolImports": [
+ {
+ "toolId": "helio/tools/repository-signals@1.0.0",
+ "skillId": "helio/skills/payments-repository-status@1.0.0",
+ "mode": "*"
+ }
+ ]
+}
diff --git a/dist/cursor/.typeference/build.json b/dist/cursor/.typeference/build.json
new file mode 100644
index 0000000..483a9c6
--- /dev/null
+++ b/dist/cursor/.typeference/build.json
@@ -0,0 +1,17 @@
+{
+ "schemaVersion": 1,
+ "target": "cursor",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "artifacts": [
+ {
+ "agentId": "helio/executive-assistant@1.0.0",
+ "path": "executive-assistant",
+ "digest": "sha256:bd14a2d263e277be46c8eaff2849c979d7140ae8da2ee2c9ca6a483d7a12f402"
+ },
+ {
+ "agentId": "helio/payments-repo-agent@1.0.0",
+ "path": "payments-repo-agent",
+ "digest": "sha256:6f37ad4627f20c79b2c565dd7c0ec8cd247c2db3417ccad7bc34de56ff7256f6"
+ }
+ ]
+}
diff --git a/dist/cursor/executive-assistant/.cursor/rules/executive-assistant.mdc b/dist/cursor/executive-assistant/.cursor/rules/executive-assistant.mdc
index 1789371..899b21b 100644
--- a/dist/cursor/executive-assistant/.cursor/rules/executive-assistant.mdc
+++ b/dist/cursor/executive-assistant/.cursor/rules/executive-assistant.mdc
@@ -16,12 +16,46 @@ Coordinates an executive's correspondence, briefings, and cross-agent requests.
## Context slots
-- `organization`: `context/organization.md`
-- `principal`: `context/principal.md`
-- `safetyPolicy`: `context/safety-policy.md`
+- `organization`: `helio/context/organization@1.0.0`
+- `principal`: `helio/context/principal@1.0.0`
+
+## Context
+
+### Executive Rhythm
+
+# Executive rhythm
+
+Daily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.
+
+### Helio Works
+
+# Helio Works
+
+Helio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.
+
+### Principal
+
+# Principal
+
+The principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.
+
+### Helio Safety Policy
+
+# Safety policy
+
+Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
## Available skills
- `executive-assistant.prepare-brief`: Assemble an executive brief, requesting repository evidence when needed.
+
+Build a decision-oriented brief from the supplied topic and evidence.
+When repository status is material, use the payments repository agent tool and
+incorporate its returned evidence with attribution.
+
- `executive-assistant.triage-message`: Classify an inbound message and recommend an accountable next action.
+Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.
+Separate facts from assumptions. Return a concise recommendation; do not send a reply.
+
+
diff --git a/dist/cursor/executive-assistant/.typeference/bundle.json b/dist/cursor/executive-assistant/.typeference/bundle.json
index c343d10..89c9add 100644
--- a/dist/cursor/executive-assistant/.typeference/bundle.json
+++ b/dist/cursor/executive-assistant/.typeference/bundle.json
@@ -10,20 +10,50 @@
"helio/interfaces/correspondent@1.0.0"
],
"slots": {
- "organization": "context/organization.md",
- "principal": "context/principal.md",
- "safetyPolicy": "context/safety-policy.md"
+ "organization": "helio/context/organization@1.0.0",
+ "principal": "helio/context/principal@1.0.0"
},
"workingNorms": [
"Preserve a clear audit trail for material decisions.",
"State uncertainty and route work to an accountable owner when authority is unclear.",
"Distinguish drafts from messages approved for delivery."
],
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/principal.md",
- "context/executive-rhythm.md"
+ "context": [
+ {
+ "id": "helio/context/executive-rhythm@1.0.0",
+ "contextType": "helio/context-types/executive-rhythm@1.0.0",
+ "satisfies": [
+ "helio/context-types/executive-rhythm@1.0.0"
+ ],
+ "content": "# Executive rhythm\n\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\n"
+ },
+ {
+ "id": "helio/context/organization@1.0.0",
+ "contextType": "helio/context-types/organization@1.0.0",
+ "satisfies": [
+ "helio/context-types/organization@1.0.0"
+ ],
+ "content": "# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n"
+ },
+ {
+ "id": "helio/context/principal@1.0.0",
+ "contextType": "helio/context-types/principal@1.0.0",
+ "satisfies": [
+ "helio/context-types/principal@1.0.0"
+ ],
+ "content": "# Principal\n\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\n"
+ },
+ {
+ "id": "helio/context/safety-policy@1.0.0",
+ "contextType": "helio/context-types/safety-policy@1.0.0",
+ "satisfies": [
+ "helio/context-types/safety-policy@1.0.0"
+ ],
+ "values": {
+ "authority": "explicit"
+ },
+ "content": "# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n"
+ }
],
"skills": [
{
@@ -31,15 +61,16 @@
"capabilityId": "helio/capabilities/prepare-brief@1.0.0",
"implementationId": "helio/skills/prepare-brief@1.0.0",
"description": "Assemble an executive brief, requesting repository evidence when needed.",
- "instructions": "Build a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, request \u0060payments-repo-agent.repository-status\u0060 and incorporate its returned evidence with attribution.\n",
+ "instructions": "Build a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, use the payments repository agent tool and\nincorporate its returned evidence with attribution.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022topic\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022topic\u0022],\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022brief\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022openQuestions\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022brief\u0022,\u0022openQuestions\u0022]}",
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/principal.md",
- "context/executive-rhythm.md"
+ "requiresContextTypes": [
+ "helio/context-types/executive-rhythm@1.0.0"
+ ],
+ "requiresTools": [
+ "helio/tools/payments-repository-agent@1.0.0"
],
+ "exposed": true,
"provenance": [
{
"field": "skill.capability",
@@ -59,11 +90,11 @@
"instructions": "Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022message\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022message\u0022],\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022summary\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022urgency\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022owner\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022nextAction\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022summary\u0022,\u0022urgency\u0022,\u0022owner\u0022,\u0022nextAction\u0022]}",
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/principal.md"
+ "requiresContextTypes": [
+ "helio/context-types/safety-policy@1.0.0"
],
+ "exposed": true,
+ "sealed": true,
"provenance": [
{
"field": "skill.capability",
@@ -81,10 +112,6 @@
"field": "slots.organization",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "slots.safetyPolicy",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
@@ -93,14 +120,6 @@
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/enterprise-defaults@1.0.0",
"source": "helio/profiles/person-defaults@1.0.0"
@@ -113,10 +132,6 @@
"field": "workingNorms",
"source": "helio/profiles/person-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/person-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/person-defaults@1.0.0",
"source": "helio/executive-assistant@1.0.0"
@@ -129,10 +144,6 @@
"field": "description",
"source": "helio/executive-assistant@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/executive-assistant@1.0.0"
- },
{
"field": "satisfies.helio/interfaces/correspondent@1.0.0",
"source": "helio/executive-assistant@1.0.0"
diff --git a/dist/cursor/executive-assistant/.typeference/link.json b/dist/cursor/executive-assistant/.typeference/link.json
new file mode 100644
index 0000000..3ee5399
--- /dev/null
+++ b/dist/cursor/executive-assistant/.typeference/link.json
@@ -0,0 +1,16 @@
+{
+ "schemaVersion": 1,
+ "agentId": "helio/executive-assistant@1.0.0",
+ "target": "cursor",
+ "defaultMode": "manual",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "dependencies": [],
+ "modes": [],
+ "toolImports": [
+ {
+ "toolId": "helio/tools/payments-repository-agent@1.0.0",
+ "skillId": "helio/skills/prepare-brief@1.0.0",
+ "mode": "*"
+ }
+ ]
+}
diff --git a/dist/cursor/executive-assistant/AGENTS.md b/dist/cursor/executive-assistant/AGENTS.md
index 8af3450..6f8c935 100644
--- a/dist/cursor/executive-assistant/AGENTS.md
+++ b/dist/cursor/executive-assistant/AGENTS.md
@@ -10,12 +10,46 @@ Coordinates an executive's correspondence, briefings, and cross-agent requests.
## Context slots
-- `organization`: `context/organization.md`
-- `principal`: `context/principal.md`
-- `safetyPolicy`: `context/safety-policy.md`
+- `organization`: `helio/context/organization@1.0.0`
+- `principal`: `helio/context/principal@1.0.0`
+
+## Context
+
+### Executive Rhythm
+
+# Executive rhythm
+
+Daily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.
+
+### Helio Works
+
+# Helio Works
+
+Helio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.
+
+### Principal
+
+# Principal
+
+The principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.
+
+### Helio Safety Policy
+
+# Safety policy
+
+Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
## Available skills
- `executive-assistant.prepare-brief`: Assemble an executive brief, requesting repository evidence when needed.
+
+Build a decision-oriented brief from the supplied topic and evidence.
+When repository status is material, use the payments repository agent tool and
+incorporate its returned evidence with attribution.
+
- `executive-assistant.triage-message`: Classify an inbound message and recommend an accountable next action.
+Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.
+Separate facts from assumptions. Return a concise recommendation; do not send a reply.
+
+
diff --git a/dist/cursor/payments-repo-agent/.cursor/rules/payments-repo-agent.mdc b/dist/cursor/payments-repo-agent/.cursor/rules/payments-repo-agent.mdc
index 371ddf3..06203fb 100644
--- a/dist/cursor/payments-repo-agent/.cursor/rules/payments-repo-agent.mdc
+++ b/dist/cursor/payments-repo-agent/.cursor/rules/payments-repo-agent.mdc
@@ -16,11 +16,42 @@ Specializes repository assistance for the fictional payments service.
## Context slots
-- `organization`: `context/organization.md`
-- `repository`: `context/repository.md`
-- `safetyPolicy`: `context/safety-policy.md`
+- `organization`: `helio/context/organization@1.0.0`
+- `repository`: `helio/context/repository-evidence@1.0.0`
+
+## Context
+
+### Helio Works
+
+# Helio Works
+
+Helio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.
+
+### Payments Service
+
+# Payments service
+
+This fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.
+
+### Repository Evidence
+
+# Repository evidence
+
+Repository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.
+
+### Helio Safety Policy
+
+# Safety policy
+
+Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
## Available skills
- `payments-repo-agent.repository-status`: Report payments-service health with contract and reconciliation evidence.
+Apply the repository-status capability for the payments service, then include
+payment-contract compatibility, reconciliation checks, and rollback readiness,
+surfacing uncertainty conversationally. Do not report the service healthy when
+any required financial-control signal is unavailable.
+
+
diff --git a/dist/cursor/payments-repo-agent/.typeference/bundle.json b/dist/cursor/payments-repo-agent/.typeference/bundle.json
index 1396aa0..e153f7a 100644
--- a/dist/cursor/payments-repo-agent/.typeference/bundle.json
+++ b/dist/cursor/payments-repo-agent/.typeference/bundle.json
@@ -10,20 +10,50 @@
"helio/interfaces/repository-observer@1.0.0"
],
"slots": {
- "organization": "context/organization.md",
- "repository": "context/repository.md",
- "safetyPolicy": "context/safety-policy.md"
+ "organization": "helio/context/organization@1.0.0",
+ "repository": "helio/context/repository-evidence@1.0.0"
},
"workingNorms": [
"Preserve a clear audit trail for material decisions.",
"State uncertainty and route work to an accountable owner when authority is unclear.",
"Prefer evidence from tests, diffs, and version control over recollection."
],
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/repository.md",
- "context/payments-service.md"
+ "context": [
+ {
+ "id": "helio/context/organization@1.0.0",
+ "contextType": "helio/context-types/organization@1.0.0",
+ "satisfies": [
+ "helio/context-types/organization@1.0.0"
+ ],
+ "content": "# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n"
+ },
+ {
+ "id": "helio/context/payments-service@1.0.0",
+ "contextType": "helio/context-types/payments-service@1.0.0",
+ "satisfies": [
+ "helio/context-types/payments-service@1.0.0"
+ ],
+ "content": "# Payments service\n\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\n"
+ },
+ {
+ "id": "helio/context/repository-evidence@1.0.0",
+ "contextType": "helio/context-types/repository-evidence@1.0.0",
+ "satisfies": [
+ "helio/context-types/repository-evidence@1.0.0"
+ ],
+ "content": "# Repository evidence\n\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\n"
+ },
+ {
+ "id": "helio/context/safety-policy@1.0.0",
+ "contextType": "helio/context-types/safety-policy@1.0.0",
+ "satisfies": [
+ "helio/context-types/safety-policy@1.0.0"
+ ],
+ "values": {
+ "authority": "explicit"
+ },
+ "content": "# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n"
+ }
],
"skills": [
{
@@ -31,15 +61,33 @@
"capabilityId": "helio/capabilities/repository-status@1.0.0",
"implementationId": "helio/skills/payments-repository-status@1.0.0",
"description": "Report payments-service health with contract and reconciliation evidence.",
- "instructions": "Apply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.\nDo not report the service healthy when any required financial-control signal is unavailable.\n",
+ "instructions": "Apply the repository-status capability for the payments service and emit only\nthe strict output object. Mark any unavailable financial-control signal as an\nexplicit null; do not report the service healthy when one is missing.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022focus\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022status\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022risks\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022status\u0022,\u0022evidence\u0022,\u0022risks\u0022]}",
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/repository.md",
- "context/payments-service.md"
+ "variants": {
+ "a2a": {
+ "instructions": "Apply the repository-status capability for the payments service and return\nattributed evidence for the calling agent \u2014 contract compatibility,\nreconciliation, and rollback readiness \u2014 with no user-facing framing. Do not\nreport the service healthy when any required financial-control signal is\nunavailable.\n",
+ "requiresContextTypes": [],
+ "requiresTools": []
+ },
+ "manual": {
+ "instructions": "Apply the repository-status capability for the payments service, then include\npayment-contract compatibility, reconciliation checks, and rollback readiness,\nsurfacing uncertainty conversationally. Do not report the service healthy when\nany required financial-control signal is unavailable.\n",
+ "requiresContextTypes": [],
+ "requiresTools": []
+ },
+ "pipeline": {
+ "instructions": "Apply the repository-status capability for the payments service and emit only\nthe strict output object. Mark any unavailable financial-control signal as an\nexplicit null; do not report the service healthy when one is missing.\n",
+ "requiresContextTypes": [],
+ "requiresTools": []
+ }
+ },
+ "requiresContextTypes": [
+ "helio/context-types/payments-service@1.0.0"
+ ],
+ "requiresTools": [
+ "helio/tools/repository-signals@1.0.0"
],
+ "exposed": true,
"provenance": [
{
"field": "skill.capability",
@@ -57,10 +105,6 @@
"field": "slots.organization",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "slots.safetyPolicy",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
@@ -69,14 +113,6 @@
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/enterprise-defaults@1.0.0",
"source": "helio/profiles/repository-defaults@1.0.0"
@@ -89,10 +125,6 @@
"field": "workingNorms",
"source": "helio/profiles/repository-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/repository-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/repository-defaults@1.0.0",
"source": "helio/payments-repo-agent@1.0.0"
@@ -105,10 +137,6 @@
"field": "description",
"source": "helio/payments-repo-agent@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/payments-repo-agent@1.0.0"
- },
{
"field": "satisfies.helio/interfaces/repository-observer@1.0.0",
"source": "helio/payments-repo-agent@1.0.0"
diff --git a/dist/cursor/payments-repo-agent/.typeference/link.json b/dist/cursor/payments-repo-agent/.typeference/link.json
new file mode 100644
index 0000000..b7f9d3a
--- /dev/null
+++ b/dist/cursor/payments-repo-agent/.typeference/link.json
@@ -0,0 +1,20 @@
+{
+ "schemaVersion": 1,
+ "agentId": "helio/payments-repo-agent@1.0.0",
+ "target": "cursor",
+ "defaultMode": "manual",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "dependencies": [],
+ "modes": [
+ "a2a",
+ "manual",
+ "pipeline"
+ ],
+ "toolImports": [
+ {
+ "toolId": "helio/tools/repository-signals@1.0.0",
+ "skillId": "helio/skills/payments-repository-status@1.0.0",
+ "mode": "*"
+ }
+ ]
+}
diff --git a/dist/cursor/payments-repo-agent/AGENTS.md b/dist/cursor/payments-repo-agent/AGENTS.md
index 915fa0a..7a318fa 100644
--- a/dist/cursor/payments-repo-agent/AGENTS.md
+++ b/dist/cursor/payments-repo-agent/AGENTS.md
@@ -10,11 +10,42 @@ Specializes repository assistance for the fictional payments service.
## Context slots
-- `organization`: `context/organization.md`
-- `repository`: `context/repository.md`
-- `safetyPolicy`: `context/safety-policy.md`
+- `organization`: `helio/context/organization@1.0.0`
+- `repository`: `helio/context/repository-evidence@1.0.0`
+
+## Context
+
+### Helio Works
+
+# Helio Works
+
+Helio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.
+
+### Payments Service
+
+# Payments service
+
+This fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.
+
+### Repository Evidence
+
+# Repository evidence
+
+Repository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.
+
+### Helio Safety Policy
+
+# Safety policy
+
+Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
## Available skills
- `payments-repo-agent.repository-status`: Report payments-service health with contract and reconciliation evidence.
+Apply the repository-status capability for the payments service, then include
+payment-contract compatibility, reconciliation checks, and rollback readiness,
+surfacing uncertainty conversationally. Do not report the service healthy when
+any required financial-control signal is unavailable.
+
+
diff --git a/dist/neutral/.typeference/build.json b/dist/neutral/.typeference/build.json
new file mode 100644
index 0000000..1bc47f6
--- /dev/null
+++ b/dist/neutral/.typeference/build.json
@@ -0,0 +1,17 @@
+{
+ "schemaVersion": 1,
+ "target": "neutral",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "artifacts": [
+ {
+ "agentId": "helio/executive-assistant@1.0.0",
+ "path": "executive-assistant",
+ "digest": "sha256:d784245d9612328f88e97780f00f72f95c95e060e21fba74ba9241fbd77e4d6f"
+ },
+ {
+ "agentId": "helio/payments-repo-agent@1.0.0",
+ "path": "payments-repo-agent",
+ "digest": "sha256:0184fa3e84f14e51bc0094076a9d5606d40fd7ab36ac1454f8177f2dd29321e2"
+ }
+ ]
+}
diff --git a/dist/neutral/executive-assistant/.typeference/link.json b/dist/neutral/executive-assistant/.typeference/link.json
new file mode 100644
index 0000000..3557224
--- /dev/null
+++ b/dist/neutral/executive-assistant/.typeference/link.json
@@ -0,0 +1,16 @@
+{
+ "schemaVersion": 1,
+ "agentId": "helio/executive-assistant@1.0.0",
+ "target": "neutral",
+ "defaultMode": "",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "dependencies": [],
+ "modes": [],
+ "toolImports": [
+ {
+ "toolId": "helio/tools/payments-repository-agent@1.0.0",
+ "skillId": "helio/skills/prepare-brief@1.0.0",
+ "mode": "*"
+ }
+ ]
+}
diff --git a/dist/neutral/executive-assistant/AGENTS.md b/dist/neutral/executive-assistant/AGENTS.md
index 8af3450..2e69ac5 100644
--- a/dist/neutral/executive-assistant/AGENTS.md
+++ b/dist/neutral/executive-assistant/AGENTS.md
@@ -10,9 +10,34 @@ Coordinates an executive's correspondence, briefings, and cross-agent requests.
## Context slots
-- `organization`: `context/organization.md`
-- `principal`: `context/principal.md`
-- `safetyPolicy`: `context/safety-policy.md`
+- `organization`: `helio/context/organization@1.0.0`
+- `principal`: `helio/context/principal@1.0.0`
+
+## Context
+
+### Executive Rhythm
+
+# Executive rhythm
+
+Daily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.
+
+### Helio Works
+
+# Helio Works
+
+Helio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.
+
+### Principal
+
+# Principal
+
+The principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.
+
+### Helio Safety Policy
+
+# Safety policy
+
+Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
## Available skills
diff --git a/dist/neutral/executive-assistant/bundle.json b/dist/neutral/executive-assistant/bundle.json
index c343d10..89c9add 100644
--- a/dist/neutral/executive-assistant/bundle.json
+++ b/dist/neutral/executive-assistant/bundle.json
@@ -10,20 +10,50 @@
"helio/interfaces/correspondent@1.0.0"
],
"slots": {
- "organization": "context/organization.md",
- "principal": "context/principal.md",
- "safetyPolicy": "context/safety-policy.md"
+ "organization": "helio/context/organization@1.0.0",
+ "principal": "helio/context/principal@1.0.0"
},
"workingNorms": [
"Preserve a clear audit trail for material decisions.",
"State uncertainty and route work to an accountable owner when authority is unclear.",
"Distinguish drafts from messages approved for delivery."
],
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/principal.md",
- "context/executive-rhythm.md"
+ "context": [
+ {
+ "id": "helio/context/executive-rhythm@1.0.0",
+ "contextType": "helio/context-types/executive-rhythm@1.0.0",
+ "satisfies": [
+ "helio/context-types/executive-rhythm@1.0.0"
+ ],
+ "content": "# Executive rhythm\n\nDaily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.\n"
+ },
+ {
+ "id": "helio/context/organization@1.0.0",
+ "contextType": "helio/context-types/organization@1.0.0",
+ "satisfies": [
+ "helio/context-types/organization@1.0.0"
+ ],
+ "content": "# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n"
+ },
+ {
+ "id": "helio/context/principal@1.0.0",
+ "contextType": "helio/context-types/principal@1.0.0",
+ "satisfies": [
+ "helio/context-types/principal@1.0.0"
+ ],
+ "content": "# Principal\n\nThe principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.\n"
+ },
+ {
+ "id": "helio/context/safety-policy@1.0.0",
+ "contextType": "helio/context-types/safety-policy@1.0.0",
+ "satisfies": [
+ "helio/context-types/safety-policy@1.0.0"
+ ],
+ "values": {
+ "authority": "explicit"
+ },
+ "content": "# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n"
+ }
],
"skills": [
{
@@ -31,15 +61,16 @@
"capabilityId": "helio/capabilities/prepare-brief@1.0.0",
"implementationId": "helio/skills/prepare-brief@1.0.0",
"description": "Assemble an executive brief, requesting repository evidence when needed.",
- "instructions": "Build a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, request \u0060payments-repo-agent.repository-status\u0060 and incorporate its returned evidence with attribution.\n",
+ "instructions": "Build a decision-oriented brief from the supplied topic and evidence.\nWhen repository status is material, use the payments repository agent tool and\nincorporate its returned evidence with attribution.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022topic\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022topic\u0022],\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022brief\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022openQuestions\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022brief\u0022,\u0022openQuestions\u0022]}",
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/principal.md",
- "context/executive-rhythm.md"
+ "requiresContextTypes": [
+ "helio/context-types/executive-rhythm@1.0.0"
+ ],
+ "requiresTools": [
+ "helio/tools/payments-repository-agent@1.0.0"
],
+ "exposed": true,
"provenance": [
{
"field": "skill.capability",
@@ -59,11 +90,11 @@
"instructions": "Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.\nSeparate facts from assumptions. Return a concise recommendation; do not send a reply.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022message\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022message\u0022],\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022summary\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022urgency\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022owner\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022nextAction\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022required\u0022:[\u0022summary\u0022,\u0022urgency\u0022,\u0022owner\u0022,\u0022nextAction\u0022]}",
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/principal.md"
+ "requiresContextTypes": [
+ "helio/context-types/safety-policy@1.0.0"
],
+ "exposed": true,
+ "sealed": true,
"provenance": [
{
"field": "skill.capability",
@@ -81,10 +112,6 @@
"field": "slots.organization",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "slots.safetyPolicy",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
@@ -93,14 +120,6 @@
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/enterprise-defaults@1.0.0",
"source": "helio/profiles/person-defaults@1.0.0"
@@ -113,10 +132,6 @@
"field": "workingNorms",
"source": "helio/profiles/person-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/person-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/person-defaults@1.0.0",
"source": "helio/executive-assistant@1.0.0"
@@ -129,10 +144,6 @@
"field": "description",
"source": "helio/executive-assistant@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/executive-assistant@1.0.0"
- },
{
"field": "satisfies.helio/interfaces/correspondent@1.0.0",
"source": "helio/executive-assistant@1.0.0"
diff --git a/dist/neutral/executive-assistant/provenance.json b/dist/neutral/executive-assistant/provenance.json
index 4bd2556..e077b7e 100644
--- a/dist/neutral/executive-assistant/provenance.json
+++ b/dist/neutral/executive-assistant/provenance.json
@@ -3,10 +3,6 @@
"field": "slots.organization",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "slots.safetyPolicy",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
@@ -15,14 +11,6 @@
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/enterprise-defaults@1.0.0",
"source": "helio/profiles/person-defaults@1.0.0"
@@ -35,10 +23,6 @@
"field": "workingNorms",
"source": "helio/profiles/person-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/person-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/person-defaults@1.0.0",
"source": "helio/executive-assistant@1.0.0"
@@ -51,10 +35,6 @@
"field": "description",
"source": "helio/executive-assistant@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/executive-assistant@1.0.0"
- },
{
"field": "satisfies.helio/interfaces/correspondent@1.0.0",
"source": "helio/executive-assistant@1.0.0"
diff --git a/dist/neutral/executive-assistant/skills/prepare-brief/SKILL.md b/dist/neutral/executive-assistant/skills/prepare-brief/SKILL.md
index 0b2f4b4..de9d46a 100644
--- a/dist/neutral/executive-assistant/skills/prepare-brief/SKILL.md
+++ b/dist/neutral/executive-assistant/skills/prepare-brief/SKILL.md
@@ -4,11 +4,5 @@ description: "Assemble an executive brief, requesting repository evidence when n
---
Build a decision-oriented brief from the supplied topic and evidence.
-When repository status is material, request `payments-repo-agent.repository-status` and incorporate its returned evidence with attribution.
-
-## Context loaded on invocation
-
-- `context/organization.md`
-- `context/safety-policy.md`
-- `context/principal.md`
-- `context/executive-rhythm.md`
+When repository status is material, use the payments repository agent tool and
+incorporate its returned evidence with attribution.
diff --git a/dist/neutral/executive-assistant/skills/triage-message/SKILL.md b/dist/neutral/executive-assistant/skills/triage-message/SKILL.md
index c349483..c2fcd1f 100644
--- a/dist/neutral/executive-assistant/skills/triage-message/SKILL.md
+++ b/dist/neutral/executive-assistant/skills/triage-message/SKILL.md
@@ -5,9 +5,3 @@ description: "Classify an inbound message and recommend an accountable next acti
Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.
Separate facts from assumptions. Return a concise recommendation; do not send a reply.
-
-## Context loaded on invocation
-
-- `context/organization.md`
-- `context/safety-policy.md`
-- `context/principal.md`
diff --git a/dist/neutral/payments-repo-agent/.typeference/link.json b/dist/neutral/payments-repo-agent/.typeference/link.json
new file mode 100644
index 0000000..fdbb0f4
--- /dev/null
+++ b/dist/neutral/payments-repo-agent/.typeference/link.json
@@ -0,0 +1,20 @@
+{
+ "schemaVersion": 1,
+ "agentId": "helio/payments-repo-agent@1.0.0",
+ "target": "neutral",
+ "defaultMode": "",
+ "sourceDigest": "sha256:981da89802beea05777dbe99b9898fa07701a7fd40534a4c0aca961fce5376ac",
+ "dependencies": [],
+ "modes": [
+ "a2a",
+ "manual",
+ "pipeline"
+ ],
+ "toolImports": [
+ {
+ "toolId": "helio/tools/repository-signals@1.0.0",
+ "skillId": "helio/skills/payments-repository-status@1.0.0",
+ "mode": "*"
+ }
+ ]
+}
diff --git a/dist/neutral/payments-repo-agent/AGENTS.md b/dist/neutral/payments-repo-agent/AGENTS.md
index 915fa0a..a514905 100644
--- a/dist/neutral/payments-repo-agent/AGENTS.md
+++ b/dist/neutral/payments-repo-agent/AGENTS.md
@@ -10,9 +10,34 @@ Specializes repository assistance for the fictional payments service.
## Context slots
-- `organization`: `context/organization.md`
-- `repository`: `context/repository.md`
-- `safetyPolicy`: `context/safety-policy.md`
+- `organization`: `helio/context/organization@1.0.0`
+- `repository`: `helio/context/repository-evidence@1.0.0`
+
+## Context
+
+### Helio Works
+
+# Helio Works
+
+Helio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.
+
+### Payments Service
+
+# Payments service
+
+This fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.
+
+### Repository Evidence
+
+# Repository evidence
+
+Repository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.
+
+### Helio Safety Policy
+
+# Safety policy
+
+Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
## Available skills
diff --git a/dist/neutral/payments-repo-agent/bundle.json b/dist/neutral/payments-repo-agent/bundle.json
index 1396aa0..e153f7a 100644
--- a/dist/neutral/payments-repo-agent/bundle.json
+++ b/dist/neutral/payments-repo-agent/bundle.json
@@ -10,20 +10,50 @@
"helio/interfaces/repository-observer@1.0.0"
],
"slots": {
- "organization": "context/organization.md",
- "repository": "context/repository.md",
- "safetyPolicy": "context/safety-policy.md"
+ "organization": "helio/context/organization@1.0.0",
+ "repository": "helio/context/repository-evidence@1.0.0"
},
"workingNorms": [
"Preserve a clear audit trail for material decisions.",
"State uncertainty and route work to an accountable owner when authority is unclear.",
"Prefer evidence from tests, diffs, and version control over recollection."
],
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/repository.md",
- "context/payments-service.md"
+ "context": [
+ {
+ "id": "helio/context/organization@1.0.0",
+ "contextType": "helio/context-types/organization@1.0.0",
+ "satisfies": [
+ "helio/context-types/organization@1.0.0"
+ ],
+ "content": "# Helio Works\n\nHelio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.\n"
+ },
+ {
+ "id": "helio/context/payments-service@1.0.0",
+ "contextType": "helio/context-types/payments-service@1.0.0",
+ "satisfies": [
+ "helio/context-types/payments-service@1.0.0"
+ ],
+ "content": "# Payments service\n\nThis fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.\n"
+ },
+ {
+ "id": "helio/context/repository-evidence@1.0.0",
+ "contextType": "helio/context-types/repository-evidence@1.0.0",
+ "satisfies": [
+ "helio/context-types/repository-evidence@1.0.0"
+ ],
+ "content": "# Repository evidence\n\nRepository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.\n"
+ },
+ {
+ "id": "helio/context/safety-policy@1.0.0",
+ "contextType": "helio/context-types/safety-policy@1.0.0",
+ "satisfies": [
+ "helio/context-types/safety-policy@1.0.0"
+ ],
+ "values": {
+ "authority": "explicit"
+ },
+ "content": "# Safety policy\n\nAgents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.\n"
+ }
],
"skills": [
{
@@ -31,15 +61,33 @@
"capabilityId": "helio/capabilities/repository-status@1.0.0",
"implementationId": "helio/skills/payments-repository-status@1.0.0",
"description": "Report payments-service health with contract and reconciliation evidence.",
- "instructions": "Apply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.\nDo not report the service healthy when any required financial-control signal is unavailable.\n",
+ "instructions": "Apply the repository-status capability for the payments service and emit only\nthe strict output object. Mark any unavailable financial-control signal as an\nexplicit null; do not report the service healthy when one is missing.\n",
"inputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022focus\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022additionalProperties\u0022:false}",
"outputSchema": "{\u0022type\u0022:\u0022object\u0022,\u0022properties\u0022:{\u0022status\u0022:{\u0022type\u0022:\u0022string\u0022},\u0022evidence\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}},\u0022risks\u0022:{\u0022type\u0022:\u0022array\u0022,\u0022items\u0022:{\u0022type\u0022:\u0022string\u0022}}},\u0022required\u0022:[\u0022status\u0022,\u0022evidence\u0022,\u0022risks\u0022]}",
- "contextFiles": [
- "context/organization.md",
- "context/safety-policy.md",
- "context/repository.md",
- "context/payments-service.md"
+ "variants": {
+ "a2a": {
+ "instructions": "Apply the repository-status capability for the payments service and return\nattributed evidence for the calling agent \u2014 contract compatibility,\nreconciliation, and rollback readiness \u2014 with no user-facing framing. Do not\nreport the service healthy when any required financial-control signal is\nunavailable.\n",
+ "requiresContextTypes": [],
+ "requiresTools": []
+ },
+ "manual": {
+ "instructions": "Apply the repository-status capability for the payments service, then include\npayment-contract compatibility, reconciliation checks, and rollback readiness,\nsurfacing uncertainty conversationally. Do not report the service healthy when\nany required financial-control signal is unavailable.\n",
+ "requiresContextTypes": [],
+ "requiresTools": []
+ },
+ "pipeline": {
+ "instructions": "Apply the repository-status capability for the payments service and emit only\nthe strict output object. Mark any unavailable financial-control signal as an\nexplicit null; do not report the service healthy when one is missing.\n",
+ "requiresContextTypes": [],
+ "requiresTools": []
+ }
+ },
+ "requiresContextTypes": [
+ "helio/context-types/payments-service@1.0.0"
+ ],
+ "requiresTools": [
+ "helio/tools/repository-signals@1.0.0"
],
+ "exposed": true,
"provenance": [
{
"field": "skill.capability",
@@ -57,10 +105,6 @@
"field": "slots.organization",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "slots.safetyPolicy",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
@@ -69,14 +113,6 @@
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/enterprise-defaults@1.0.0",
"source": "helio/profiles/repository-defaults@1.0.0"
@@ -89,10 +125,6 @@
"field": "workingNorms",
"source": "helio/profiles/repository-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/repository-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/repository-defaults@1.0.0",
"source": "helio/payments-repo-agent@1.0.0"
@@ -105,10 +137,6 @@
"field": "description",
"source": "helio/payments-repo-agent@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/payments-repo-agent@1.0.0"
- },
{
"field": "satisfies.helio/interfaces/repository-observer@1.0.0",
"source": "helio/payments-repo-agent@1.0.0"
diff --git a/dist/neutral/payments-repo-agent/provenance.json b/dist/neutral/payments-repo-agent/provenance.json
index 6a5b701..5d11255 100644
--- a/dist/neutral/payments-repo-agent/provenance.json
+++ b/dist/neutral/payments-repo-agent/provenance.json
@@ -3,10 +3,6 @@
"field": "slots.organization",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "slots.safetyPolicy",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
@@ -15,14 +11,6 @@
"field": "workingNorms",
"source": "helio/profiles/enterprise-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
- {
- "field": "contextFiles",
- "source": "helio/profiles/enterprise-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/enterprise-defaults@1.0.0",
"source": "helio/profiles/repository-defaults@1.0.0"
@@ -35,10 +23,6 @@
"field": "workingNorms",
"source": "helio/profiles/repository-defaults@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/profiles/repository-defaults@1.0.0"
- },
{
"field": "embeds.helio/profiles/repository-defaults@1.0.0",
"source": "helio/payments-repo-agent@1.0.0"
@@ -51,10 +35,6 @@
"field": "description",
"source": "helio/payments-repo-agent@1.0.0"
},
- {
- "field": "contextFiles",
- "source": "helio/payments-repo-agent@1.0.0"
- },
{
"field": "satisfies.helio/interfaces/repository-observer@1.0.0",
"source": "helio/payments-repo-agent@1.0.0"
diff --git a/dist/neutral/payments-repo-agent/skills/repository-status/SKILL.a2a.md b/dist/neutral/payments-repo-agent/skills/repository-status/SKILL.a2a.md
new file mode 100644
index 0000000..5b6e7b0
--- /dev/null
+++ b/dist/neutral/payments-repo-agent/skills/repository-status/SKILL.a2a.md
@@ -0,0 +1,10 @@
+---
+name: repository-status
+description: "Report payments-service health with contract and reconciliation evidence."
+---
+
+Apply the repository-status capability for the payments service and return
+attributed evidence for the calling agent — contract compatibility,
+reconciliation, and rollback readiness — with no user-facing framing. Do not
+report the service healthy when any required financial-control signal is
+unavailable.
diff --git a/dist/neutral/payments-repo-agent/skills/repository-status/SKILL.manual.md b/dist/neutral/payments-repo-agent/skills/repository-status/SKILL.manual.md
new file mode 100644
index 0000000..b0c22ea
--- /dev/null
+++ b/dist/neutral/payments-repo-agent/skills/repository-status/SKILL.manual.md
@@ -0,0 +1,9 @@
+---
+name: repository-status
+description: "Report payments-service health with contract and reconciliation evidence."
+---
+
+Apply the repository-status capability for the payments service, then include
+payment-contract compatibility, reconciliation checks, and rollback readiness,
+surfacing uncertainty conversationally. Do not report the service healthy when
+any required financial-control signal is unavailable.
diff --git a/dist/neutral/payments-repo-agent/skills/repository-status/SKILL.md b/dist/neutral/payments-repo-agent/skills/repository-status/SKILL.md
index 9e8fcf8..be8d47b 100644
--- a/dist/neutral/payments-repo-agent/skills/repository-status/SKILL.md
+++ b/dist/neutral/payments-repo-agent/skills/repository-status/SKILL.md
@@ -3,12 +3,6 @@ name: repository-status
description: "Report payments-service health with contract and reconciliation evidence."
---
-Apply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.
-Do not report the service healthy when any required financial-control signal is unavailable.
-
-## Context loaded on invocation
-
-- `context/organization.md`
-- `context/safety-policy.md`
-- `context/repository.md`
-- `context/payments-service.md`
+Apply the repository-status capability for the payments service and emit only
+the strict output object. Mark any unavailable financial-control signal as an
+explicit null; do not report the service healthy when one is missing.
diff --git a/dist/neutral/payments-repo-agent/skills/repository-status/SKILL.pipeline.md b/dist/neutral/payments-repo-agent/skills/repository-status/SKILL.pipeline.md
new file mode 100644
index 0000000..be8d47b
--- /dev/null
+++ b/dist/neutral/payments-repo-agent/skills/repository-status/SKILL.pipeline.md
@@ -0,0 +1,8 @@
+---
+name: repository-status
+description: "Report payments-service health with contract and reconciliation evidence."
+---
+
+Apply the repository-status capability for the payments service and emit only
+the strict output object. Mark any unavailable financial-control signal as an
+explicit null; do not report the service healthy when one is missing.
diff --git a/docs/assets/compiler-pipeline.svg b/docs/assets/compiler-pipeline.svg
index db95212..394b628 100644
--- a/docs/assets/compiler-pipeline.svg
+++ b/docs/assets/compiler-pipeline.svg
@@ -7,5 +7,5 @@
Codex
Copilot
- Cursor + MCP
+ Cursor + Neutral
diff --git a/docs/assets/cross-agent.svg b/docs/assets/cross-agent.svg
index 41b04a6..6f0296d 100644
--- a/docs/assets/cross-agent.svg
+++ b/docs/assets/cross-agent.svg
@@ -1,11 +1,11 @@
diff --git a/docs/assets/dispatch.svg b/docs/assets/dispatch.svg
index 8239618..5c11865 100644
--- a/docs/assets/dispatch.svg
+++ b/docs/assets/dispatch.svg
@@ -1,7 +1,7 @@
diff --git a/docs/assets/interoperability-stack.svg b/docs/assets/interoperability-stack.svg
index 98483bd..81ffd43 100644
--- a/docs/assets/interoperability-stack.svg
+++ b/docs/assets/interoperability-stack.svg
@@ -1,6 +1,6 @@
diff --git a/docs/decisions/0012-invocation-mode-skill-variants.md b/docs/decisions/0012-invocation-mode-skill-variants.md
new file mode 100644
index 0000000..b9c6182
--- /dev/null
+++ b/docs/decisions/0012-invocation-mode-skill-variants.md
@@ -0,0 +1,103 @@
+# 0012 — Invocation-mode skill variants
+
+**Status:** Accepted (2026-07-23)
+
+## Context
+
+A capability is a fixed contract: a versioned method slot with a stable
+`inputSchema`/`outputSchema`. A skill is a concrete implementation that binds one
+capability.
+
+Real agents need the *same* capability delivered differently to different
+consumers. The motivating case is a repository-status agent whose output is
+consumed three ways:
+
+- **pipeline** — automated/CI invocation; strict machine output, no prose, never
+ asks a clarifying question, marks unavailable signals explicitly.
+- **manual** — a human in an interactive host (Copilot, Cursor); explains
+ reasoning, surfaces risk conversationally, offers the next accountable action.
+- **a2a** — invoked by another agent (the cross-agent request path Helio already
+ exercises when `prepare-brief` requests `payments-repo-agent.repository-status`);
+ returns attributed evidence without user-facing framing.
+
+These three share the capability's I/O contract exactly. Only the
+instructions/persona/delivery differ. The question is where that variation lives.
+
+The decisive observation: **mode does not change the contract.** A concern that
+never touches `inputSchema`/`outputSchema` has no business living on the capability
+or on the universal binding. It belongs on the one skill whose *rendering* varies.
+
+## Decision
+
+1. **`variants` is an opt-in property of a skill.** A skill declares *either*
+ `instructions` (unimodal — the default, unchanged, the common case) *or*
+ `variants:` — a mapping of mode name to `{ instructions, contextFiles?,
+ requiresContextTypes?, tools? }`. Skills with no modes are untouched and pay no
+ ceremony.
+
+ ```yaml
+ kind: skill
+ id: helio/skills/repository-status@1.0.0
+ binds: helio/capabilities/repository-status@1.0.0
+ inputSchema: '...' # shared across variants
+ outputSchema: '...' # shared across variants
+ variants:
+ pipeline: { instructions: "Emit strict JSON, no prose; mark missing signals null." }
+ manual: { instructions: "Explain reasoning, surface risks, offer next action." }
+ a2a: { instructions: "Return attributed evidence for a calling agent." }
+ ```
+
+2. **The capability and the binding are unchanged.** No `mode` appears on the
+ capability or on `SkillBinding`. A variant may set `instructions`,
+ `contextFiles`/context references, `requiresContextTypes` (ADR-0013), and tool
+ bindings (ADR-0017) — it may **not** override `inputSchema`/`outputSchema`.
+ Forking the contract is the one thing mode must never do, and the loader rejects
+ it.
+
+3. **The resolver's slot/ambiguity model is untouched.** A multimodal skill still
+ fills its capability slot exactly once; the promoted-name ambiguity check needs
+ no special case. Only the **emitter** fans a multimodal skill into one artifact
+ per variant. This is why this shape is preferred over a per-binding `mode`
+ discriminator, which would have forced the ambiguity checker to special-case
+ "same capability, different mode".
+
+4. **Variants may narrow context and tool requirements — the governance hook.** A
+ variant may raise `requiresContextTypes` to a stricter refinement (ADR-0013) and
+ bind a differently-scoped tool (ADR-0017): the `a2a`/`pipeline` variants can
+ *require* a governed context type and a governed-scope tool while `manual`
+ accepts the base. This is where "personal reads the personal vault, a2a reads
+ the governed one" is expressed as a **type constraint per variant**, not a
+ hardcoded path.
+
+5. **Variant selection is target-driven with an override.** Each target adapter
+ declares the default variant for the invocation surface it represents
+ (Copilot/Cursor → `manual`, MCP-callable/card → `a2a`, neutral/CI → `pipeline`),
+ overridable by a build flag. A target consuming a unimodal skill is unchanged.
+
+6. **Mode names are an open, author-defined vocabulary** with a recommended core
+ set (`pipeline`, `manual`, `a2a`) that target adapters key their defaults on.
+
+## Consequences
+
+- Authoring stays DRY: one agent, one skill, N faces. The distinction lives in the
+ type system, not in duplicated filenames.
+- The common case (a skill with no modes) is entirely unaffected.
+- **Cross-constraint with the on-disk format:** when TypeFerence source is written
+ as frontmatter-plus-body (the deferred `.tfer` serialization, referenced in
+ ADR-0013), "body = the skill's instructions" works only for a *unimodal* skill. A
+ multimodal skill's per-variant instructions live in structured frontmatter; the
+ body is either unused or carries the default variant. The format decision must be
+ written aware of this.
+- Work required: a `variants` field in the loader/model with the "no schema
+ override" guard; emitter fan-out and per-adapter default-variant declaration; a
+ spec section; a golden-file fixture exercising a multimodal skill across targets.
+
+## Alternatives considered
+
+- **Agent-per-mode.** Duplicates the entire agent to vary one skill — the sprawl
+ TypeFerence exists to remove. Rejected as off-thesis.
+- **`mode` on every `SkillBinding`.** Pollutes the ~90% of bindings that have no
+ modes and forces the ambiguity checker to special-case same-capability
+ multi-binding. Rejected.
+- **`mode` in `inputSchema`.** Relocates the polymorphism to runtime, which the
+ definition layer does not model. Rejected.
diff --git a/docs/decisions/0013-user-defined-typed-context.md b/docs/decisions/0013-user-defined-typed-context.md
new file mode 100644
index 0000000..f8819d8
--- /dev/null
+++ b/docs/decisions/0013-user-defined-typed-context.md
@@ -0,0 +1,105 @@
+# 0013 — User-defined typed context
+
+**Status:** Accepted (2026-07-23)
+
+## Context
+
+Context today is untyped. `contextFiles` is a list of bare relative paths, and the
+loader only checks that each path exists and does not escape the source root
+(`go/internal/resource/loader.go`). Nothing distinguishes a principal-preferences
+note from a safety policy from a "cast of characters" roster — they are all just
+markdown at a path. It is also the one place the type system reaches *outside*
+itself: a raw filesystem pointer to an opaque blob, the assembly-language escape
+hatch inside the higher-level language.
+
+Two things are missing:
+
+1. **Naming a *kind* of context** — "cast of characters," "data-classification," a
+ particular vault's note shape — without that kind being a hardcoded TypeFerence
+ feature. TypeFerence ships the type *system*, not the types.
+2. **Typing the boundary between a component and the knowledge it carries**, so a
+ mismatch is a build-time error rather than a runtime surprise.
+
+The clarifying frame: context is the **fields** half of the object model
+(capabilities/skills are the methods; ADR-0015/0017). An agent *holds* typed
+context; a skill *requires* the context types it needs to operate.
+
+## Decision
+
+1. **New resource kind `contextType`** — a first-class, versioned, user-defined
+ type. `id`, `displayName`, `description`, and an optional `schema` (JSON Schema
+ over the note's frontmatter). With `schema`, conformance is checked
+ structurally; without it, the type is a name tag (still useful for the gating in
+ decisions 4–5). "Cast of characters" and a vault's note shape are thus *defined
+ in YAML by a user*, never a language feature.
+
+2. **Context types embed/refine structurally, like interfaces.** A
+ `governedCastOfCharacters` may embed `castOfCharacters` and add provenance/owner
+ frontmatter; a governed roster *is a* cast of characters (satisfies the base).
+ This reuses the interface satisfaction machinery and is what makes a **trust
+ boundary a type** rather than a path: a variant or capability that requires the
+ governed refinement structurally rejects a bare personal note (ADR-0012 §4).
+
+3. **Agents/profiles HOLD context; skills REQUIRE context types.** Holding typed
+ context is agent/profile state (`context:`, formerly `contextFiles`). A skill
+ declares the types it needs:
+
+ ```yaml
+ # skill
+ requiresContextTypes: [acme/context-types/my-obsidian-shape@1.0.0]
+ ```
+
+ The resolver checks that the holding component supplies context satisfying the
+ skill's required types — the same provide/require/check shape capabilities
+ already have. This is a *wiring* constraint, not a contract change: two skills
+ requiring different context types remain substitutable at their capability.
+
+4. **Governance gating.** A profile/agent/capability may declare
+ `allowedContextTypes` (a whitelist; anything outside it — direct, promoted, or
+ slot-filled — is a build error) and/or `requiresContextTypes` (must be present
+ after composition). Allow-lists **intersect** through embeds (the most
+ restrictive ancestor wins; embedding narrows, never widens).
+
+5. **Reference context by id, not by path.** `contextFiles: [context/x.md]` is
+ deprecated in favor of `context: [acme/notes/x@1.0.0]`, resolved through the
+ resource map like `embeds`/`skills`. This closes the last raw-path reference: a
+ context object is a first-class resource (`kind: context`) with an `id`, a
+ `contextType`, typed fields, and prose. Typed slots accept a context type, so
+ "fill the `vault` slot" becomes "fill it with a note of type X".
+
+6. **Types come from the type system, not the substrate.** Frontmatter/field values
+ are interpreted **schema-directed** against the declaring `contextType` — so a
+ `country: no` field is the string `"no"`, never the boolean, and YAML's implicit
+ coercions never fire. Determinism holds: contextType ids sort into the canonical
+ key space, field values canonicalize before hashing, and provenance records
+ which component contributed each typed context.
+
+## Consequences
+
+- "Cast of characters," "data-classification," and a vault's shape become things a
+ user *defines*, not things TypeFerence *has*.
+- Context joins the type system fully — no raw path references remain.
+- A real governance primitive: a profile or capability can constrain or require the
+ *kinds* of context flowing into anything built on it, the context analogue of
+ structural capability typing.
+- The zero-ceremony default is preserved: untyped context keeps working; every new
+ field is opt-in.
+- **The on-disk serialization is deferred.** How a context object is written on
+ disk (frontmatter-plus-body, a distinct `.tfer` extension, schema-directed
+ parsing, verbatim body, and whether Obsidian authors it natively) is a separate,
+ currently-parked decision — the *type semantics* here do not depend on it. The
+ reference-by-id and schema-directed-typing rulings above are the load-bearing
+ parts; the file format ADR will follow when the extension question is settled.
+- Compile-time typed context is *Door A* (baked into artifacts); a context *type*
+ may also describe data a tool reads at runtime (*Door B*), where TypeFerence types
+ the shape but does not ship the reader (ADR-0019).
+
+## Alternatives considered
+
+- **Hardcode a fixed context taxonomy.** The exact anti-pattern the project rejects.
+ Ship the type system, not the types. Rejected.
+- **Reference-side typing only** (`{ path, type }`, no id). Keeps the raw path and
+ lets a note be silently retyped per reference. Rejected in favor of first-class
+ context resources referenced by id.
+- **Type context purely nominally.** Kept as the zero-schema case, but the optional
+ `schema` is what makes `requiresContextTypes` more than a naming convention.
diff --git a/docs/decisions/0014-go-only-implementation.md b/docs/decisions/0014-go-only-implementation.md
new file mode 100644
index 0000000..9c6541c
--- /dev/null
+++ b/docs/decisions/0014-go-only-implementation.md
@@ -0,0 +1,80 @@
+# 0014 — Go-only implementation; the spec is the open invitation
+
+**Status:** Accepted (2026-07-23)
+
+Supersedes the dual-implementation commitments in ADR-0003, ADR-0004, and
+ADR-0005 (the canonicalization rulings and the fixture corpus survive; the
+requirement that *two* implementations agree does not).
+
+## Context
+
+TypeFerence began as two implementations — a C# reference (`src/`) and an
+independent Go implementation (`go/`) — held to byte-identity by a shared
+conformance suite (ADR-0005). C# came first because it was the author's more
+familiar language; the model being compiled (Go-like embedding, structural
+interfaces, promoted-name ambiguity) is Go-shaped. The C# implementation also
+carries the only `serve` (MCP runtime) surface.
+
+Two implementations proved the specification is *normative* rather than a
+description of one program's behavior. But that credential serves a constituency —
+*other people who implement TypeFerence* — that does not currently exist, and it is
+paid for on **every feature**: every change is built twice and held byte-identical.
+The layer TypeFerence targets is still up for debate, and velocity on the tool
+(compiler, LSP, `.tfer` tooling, Obsidian integration) is what earns adoption. None
+of that is served by a second implementation.
+
+The decisive distinction: **conformance is not determinism.**
+
+- *Determinism* — one compiler produces byte-identical output from identical input —
+ is what makes `diff` meaningful, the committed-digest demos real, and governance
+ possible. It is a property of one good compiler.
+- *Conformance* — two independent implementations agree — is the part that serves
+ the non-existent implementer constituency.
+
+Dropping the second implementation loses only the latter.
+
+## Decision
+
+1. **Go is the sole implementation.** Retire the C# reference implementation
+ (`src/`, `tests/`, `TypeFerence.slnx`, `Directory.Build.props`) and the C# CI/
+ conformance jobs. One codebase, full velocity.
+
+2. **The specification remains the source of truth.** `docs/specification.md` stays
+ normative *in principle* — "here is the abstraction; realize it differently if
+ you want, but this repository's Go compiler is the one living answer." This keeps
+ the standard door open (a future second implementation could reappear) without
+ staffing it now, and preserves the accurate framing that TypeFerence is a *spec
+ with a reference implementation*, not merely a program.
+
+3. **The conformance suite becomes a Go golden-file determinism suite.** The
+ `conformance/fixtures/` corpus and canonicalization rulings (ADR-0004) survive
+ unchanged; the suite is re-pointed to prove "the Go compiler still emits the
+ committed bytes" instead of "Go and C# agree." The digest guarantee — the
+ property that matters — is fully retained.
+
+4. **`serve` is not ported (ADR-0018).** It is off-thesis (deployment/runtime), and
+ its value is reframed as *emitting a static callable-resource card*, not running
+ a server. Its retirement with C# costs the core nothing.
+
+## Consequences
+
+- Full-velocity single codebase; every subsequent ADR in this batch is implemented
+ once, not twice.
+- Determinism, digests, `diff`, and provenance are unaffected.
+- The `.slnx`/.NET toolchain requirement disappears from build and CI.
+- The one concrete loss is `serve` as a working runtime; superseded by the
+ callable-card emission (ADR-0018).
+- The spec is downgraded from "normative, proven by two implementations" to
+ "normative in principle, realized by one." Recoverable if a second implementation
+ ever returns; deliberately reversible.
+
+## Alternatives considered
+
+- **Keep dual-impl.** Justified only if TypeFerence's ambition is a *standard others
+ implement*. For a *tool people install*, it is a recurring tax on the wrong axis.
+ Rejected in favor of the enablement-first posture.
+- **Collapse to "a Go package," drop the spec.** Fastest, but bricks the standard
+ door and downgrades the spec to "whatever the Go code does." Rejected: keeping the
+ spec doc costs almost nothing and preserves optionality.
+- **Port `serve` to Go.** The most expensive port for the least critical-path value,
+ and off-thesis regardless. Rejected (ADR-0018).
diff --git a/docs/decisions/0015-exposure-and-visibility.md b/docs/decisions/0015-exposure-and-visibility.md
new file mode 100644
index 0000000..bff193e
--- /dev/null
+++ b/docs/decisions/0015-exposure-and-visibility.md
@@ -0,0 +1,61 @@
+# 0015 — Exposure and visibility
+
+**Status:** Accepted (2026-07-23)
+
+## Context
+
+"Authoring an MCP tool" has felt like a missing concept. It is not: **MCP is the
+wire projection of a capability across the definition boundary** (ADR-0018), and you
+never author an MCP — you author a capability and *decide whether it is a public
+entry point*.
+
+Most capabilities are internal composition plumbing: they exist to compose behavior
+within an agent and nothing outside ever names them. A few are entry points meant to
+be called by a consumer the definition does not control — another agent, a host
+tool-loop, an external service. Today there is no way to say which is which, and the
+existing C# `serve` exposed *every* skill as a tool, leaking internal plumbing as
+public API.
+
+This is precisely Go's exported/unexported distinction — the public-API boundary —
+and it should be modelled the same way.
+
+## Decision
+
+1. **Visibility is a per-capability property: `internal` (default) or `exposed`.**
+ Only `exposed` capabilities become part of an agent's public callable surface and
+ are projected into callable-resource cards (ADR-0018). This is Go's export model:
+ the boundary between public API and package-private plumbing.
+
+2. **Exposure is declared at the definition and promoted through embedding**, the
+ way Go promotes exported members of an embedded struct. An embedded profile's
+ exposed capabilities are promoted onto the embedder's public surface; internal
+ ones are not. This rides the promotion/ambiguity machinery the resolver already
+ has — visibility is one added attribute on the promoted member.
+
+3. **Default is `internal` (private-by-default), matching Go's unexported default.**
+ Exposing mints a *stable public API* — a name and schema outside callers depend
+ on — which is expensive to walk back; accidentally exposing all internal plumbing
+ is a worse, less-reversible trap than accidentally leaving something callable
+ internally. Private-by-default fails safe here. (Contrast the mutability axis in
+ ADR-0016, which is *open*-by-default; the unifying rule is per-axis Go defaults:
+ **open on override, private on export.**)
+
+4. **Exposure is a commitment, surfaced by tooling.** Because exposing is minting
+ public API, `inspect`/the LSP show which capabilities are exposed, and `diff`
+ flags an exposure change as the API-surface change it is.
+
+## Consequences
+
+- The only new authoring surface is a one-bit visibility marker; the default (all
+ internal) means an agent exposes nothing until deliberately opened.
+- Callable-resource cards (ADR-0018) carry exactly the exposed capabilities — no
+ more blanket dumps of internal skills.
+- Exposure composes through embedding without new machinery.
+
+## Alternatives considered
+
+- **A central `exposes:` list on the agent.** Less Go-native than
+ visibility-at-definition-plus-promotion, and it fragments the public surface away
+ from where members are declared. Rejected in favor of the promotion model.
+- **Expose-by-default.** Turns internal composition into public API by omission — the
+ expensive, irreversible failure. Rejected.
diff --git a/docs/decisions/0016-sealing-mutability-presence.md b/docs/decisions/0016-sealing-mutability-presence.md
new file mode 100644
index 0000000..1b63ce4
--- /dev/null
+++ b/docs/decisions/0016-sealing-mutability-presence.md
@@ -0,0 +1,103 @@
+# 0016 — Sealing: mutability and presence
+
+**Status:** Accepted (2026-07-23)
+
+## Context
+
+A security (or platform) team authors a profile that others embed. Some of its
+skills are meant to be extension points others may override; others are guarantees
+that must survive composition unmodified — if you mix in the safety profile, you use
+its safety control as-is. Go's model does not support this: embedding promotes, but
+an embedder may *shadow* a promoted member by redeclaring it. There is no `final`.
+That is exactly what a governance foundation cannot allow — any embedder could
+silently shadow the safety control and its guarantee would evaporate.
+
+This is the first place the Go aesthetic genuinely breaks: Go trusts the programmer,
+so it has no `final`; a governance-composition layer cannot, because the whole point
+is that one team's control survives another team's mix-in intact.
+
+## Decision
+
+1. **`sealed` modifier (mutability axis: `open` default / `sealed`).** A sealed
+ capability/skill may not be overridden, rebound, or suppressed by an embedder;
+ attempting to is a compile error. Crucially, **sealed forecloses *modification*,
+ not *extension*.** It is a `final` *member* on an *open* container: the profile
+ stays embeddable, you may add adjacent capabilities freely, you simply cannot dig
+ under the guaranteed member. Sealed member ≠ closed container.
+
+2. **The three operations sealed governs:** *add adjacent behavior* (always
+ allowed — this is extensibility, intact); *override/rebind the sealed capability*
+ (compile error); *remove/suppress it* (compile error).
+
+3. **`required` modifier (presence axis: `optional` default / `required`).** A
+ required capability must be *bound* in every concrete agent that carries the
+ mandate. Because embedding only ever promotes — nothing in the model can drop
+ a binding — "cannot be dropped" is not where this axis earns its keep. The
+ operation it governs is the opposite one: **a mandate may be declared without
+ a fulfillment.** A binding that omits `ref` and names a `capability` is an
+ **abstract requirement**: the profile demands the capability, an embedder
+ supplies it. A ref-less binding is meaningless unless `required`, so that
+ combination is rejected rather than silently ignored.
+
+ Enforcement is asymmetric by kind, which is what makes profiles abstract:
+ a **profile** MAY carry unfulfilled requirements; an **agent** is concrete and
+ MUST bind every capability mandated by itself or anything it embeds, or
+ composition fails. Requirements propagate through intermediate profiles.
+
+ `required` is *presence only*: overriding a required-but-open capability is
+ legal, since the capability is still present. What fills it is `sealed`'s
+ concern. `sealed + required` is therefore the mandatory control: must be
+ carried *and* cannot be modified.
+
+ This is the demand side of the type system, which nothing else supplied:
+ `satisfies` is inferred structurally, so interfaces only *observe* whether a
+ capability is present. `required` is the only primitive that can *require* it.
+
+4. **Open-by-default, enablement-first.** Everything is `open` unless explicitly
+ `sealed`. The people who need sealing (governance authors) are exactly the ones
+ who will remember to seal; open-by-default keeps ordinary composition frictionless
+ and keeps the divergence from Go *itself* opt-in — the type system is 100%
+ Go-shaped until someone explicitly reaches for the one primitive Go lacks.
+ (Combined with ADR-0015: per-axis Go defaults — **open on override, private on
+ export**.)
+
+5. **No special root.** A profile is a profile; "root" is only graph position
+ (embeds nothing / is embedded), not a distinct kind. Sealing is a uniform
+ modifier available on any profile/skill, never a foundation-only feature.
+
+6. **Sealing later is a breaking change; make it observable, not default.** Going
+ `open → sealed` can break embedders who were overriding. This is managed by
+ *visibility of seal state* — `inspect`/the LSP show sealed vs open, and `diff`
+ flags the transition — not by a stricter default.
+
+## Consequences
+
+- Governance foundations become real: a control can be handed to embedders that they
+ may build on but not weaken, enforced at compile time.
+- Enablement stays the default; security is *achievable and observable* without
+ taxing ordinary composition.
+- Some compositions become **illegal, by design**: two profiles that seal the same
+ capability to *different* skills cannot be embedded together (a hard error that
+ surfaces a real governance conflict). A sealed diamond (the same sealed member
+ reached by two paths) is conceptually fine — one member, no conflict — but note
+ the resolver's pre-existing same-depth promotion check currently rejects *any*
+ equal-depth diamond (sealed or not) with an ambiguity error before sealing is
+ considered; lifting that limitation is separate work.
+- Three orthogonal axes now exist: visibility (ADR-0015), mutability, presence — all
+ permissive by default, every lock opt-in.
+- Profiles gain an abstract-base-class shape: a governance profile can hand down an
+ obligation ("every agent must bind an audit control") without dictating the
+ implementation, which `sealed` alone cannot express — sealing protects a control
+ you supply, `required` demands one you do not.
+- Modifier state is emitted into bundles when a lock is engaged (absent means the
+ axis is at its permissive default), so `inspect` can show it and `diff` can flag
+ an `open -> sealed` or `optional -> required` transition.
+
+## Alternatives considered
+
+- **Sealed-by-default for security foundations (fail-closed).** Protects authors who
+ forget to seal — but those are exactly the authors who don't need protection, and
+ it taxes every ordinary profile and breaks the uniform "no special root" rule.
+ Rejected in favor of open-by-default plus seal-state observability.
+- **No sealing (stay pure Go).** Leaves governance guarantees unenforceable under
+ composition. Rejected; this is the deliberate, minimal divergence.
diff --git a/docs/decisions/0017-tools-as-extern.md b/docs/decisions/0017-tools-as-extern.md
new file mode 100644
index 0000000..c60a2df
--- /dev/null
+++ b/docs/decisions/0017-tools-as-extern.md
@@ -0,0 +1,83 @@
+# 0017 — Tools as extern declarations
+
+**Status:** Accepted (2026-07-23)
+
+## Context
+
+Skills reference runtime tools — a vault reader, an API client — and today those
+tools "float": a skill hopes the tool is available at runtime, with no typed
+relationship. The root cause is that a tool has only a runtime **body** (the code
+that runs, off in deployment) and no compile-time **declaration**. It is the same
+untyped-reference problem ADR-0013 fixed for context, one layer over.
+
+There is also a vocabulary question. Checked against the MCP tools specification:
+MCP uses **"capabilities"** for feature negotiation (a handshake) and **"tool"** for
+an executable callable, and it *fuses* contract and implementation into that one
+word. TypeFerence deliberately keeps them separate — which resolves "are we blending
+skills and tools?": no, they are siblings.
+
+## Decision
+
+1. **`tool` is a first-class kind: an extern / header.** You author the tool's
+ *signature* — its `id`, interface (`inputSchema`/`outputSchema`), and the
+ auth/scope *shape* it demands — in the source. It carries **no context**. The
+ *body* (the code that actually reads the vault or calls the API) is implemented
+ outside the compilation unit, at runtime, by whoever deploys — exactly like a C
+ header, a `.d.ts`, or a Go interface for an external service. This reconciles
+ "the user writes the tool" (the body) with "tools are authored natively" (the
+ declaration): different halves.
+
+2. **The trio, un-fused (the object model's method layer):**
+ - **capability** — the contract ("what"); the *signature* half of an MCP tool.
+ - **skill** — **model**-fulfillment of a capability (the LLM does it via
+ instructions/context); *no MCP equivalent*.
+ - **tool** — **code**-fulfillment of a capability (an extern; the *implementation*
+ half of an MCP tool).
+
+ Skill and tool are **siblings** — two fulfillment modalities of one capability,
+ not a blend. Previously only the model sibling existed; `tool` completes the pair.
+
+3. **Skills declare typed tool dependencies (`requiresTools`), compile-checked.** A
+ skill (or variant) declaring `requiresTools: [acme/tools/vault-reader@1.0.0]` is
+ verified at build: the tool is declared and its interface shape-matches. Runtime
+ hope becomes a build error. TypeFerence still never runs the tool.
+
+4. **One tool, auth/scope varies per scoped skill extension.** A tool is declared
+ once; each extension (an ADR-0012 variant) binds it with a different **scope and
+ credential *reference***, never the secret. The `manual`/personal extension binds
+ personal scope; the `a2a`/governed extension binds governed scope — and a governed
+ extension can be *required* to bind a governed-scope tool, enforced structurally
+ (the same "trust boundary is a type" move as ADR-0013 refinement). This matches
+ the MCP model, which says the tool set "MAY vary by the authorization presented on
+ the request — credentials are per-request input, not connection state."
+
+5. **Boundary rule:** TypeFerence types the **extern** (declaration, dependency,
+ scope-binding); it does not own the **body** (implementation, secrets, execution).
+ Same line as "TypeFerence embeds no LLM provider."
+
+6. **MCP naming crosswalk (also referenced by ADR-0018):** our **capability →** an
+ MCP **tool** (signature); MCP **"capabilities"** is a transport-layer negotiation
+ handshake *below* the TypeFerence layer and is **not** our `capability`. We keep
+ `capability` internally (it does coherent Go-interface-method work, and `interface`
+ is already a separate kind) and publish this crosswalk rather than renaming.
+
+## Consequences
+
+- Tools stop floating: every skill→tool dependency is declared and shape-checked at
+ build; no runtime "hope it's there."
+- The method layer of the object model is complete and symmetric: one contract
+ (capability), two implementations (skill = model, tool = code).
+- Auth is expressed as typed scope/credential references per extension; secrets never
+ enter the definition, consistent with the no-credentials boundary.
+- Runtime tools (Door B, ADR-0019) are typed here but implemented by the deployer.
+
+## Alternatives considered
+
+- **Keep tools implicit/hoped-for at runtime.** The floating status quo; no
+ compile-time enforcement. Rejected.
+- **Model a tool as just a capability with an "external" flag.** Understates it: a
+ tool needs an auth/scope shape and is the *code-fulfillment* sibling of a skill, not
+ merely a marked capability. Rejected in favor of a first-class extern kind.
+- **Rename `capability` to avoid the MCP collision.** Churns a core, internally
+ coherent word (and collides with the existing `interface` kind). Rejected in favor
+ of a documented crosswalk.
diff --git a/docs/decisions/0018-callable-resource-card-and-publishing.md b/docs/decisions/0018-callable-resource-card-and-publishing.md
new file mode 100644
index 0000000..562c8d7
--- /dev/null
+++ b/docs/decisions/0018-callable-resource-card-and-publishing.md
@@ -0,0 +1,78 @@
+# 0018 — Callable-resource card and publishing
+
+**Status:** Accepted (2026-07-23)
+
+## Context
+
+The C# `serve` verb stood up an MCP server from compiled agents, turning each skill
+into a live tool whose calls returned a deterministic invocation package. ADR-0014
+retires it as off-thesis runtime. But its *value* is not the running process — it is
+a **packaging** decision, and that value should be recovered as a deterministic
+emission rather than a server.
+
+Meanwhile `--emit-ard` already publishes ARD catalog entries — a source-package
+entry and a precompiled-bundle entry per concrete agent/target — but it *explicitly
+punts* on callable resources: the README notes that callable MCP/A2A resources
+"require their own deployed endpoint and native card." `serve` assembled exactly that
+native card in memory and then threw it away by only serving it live. The two nearly
+conflict; in fact they complete each other.
+
+The design boundary is firm: **no ARD registry lifecycle, federation, dependency, or
+deployment metadata in core semantics.** So emission is core; anything that *pushes*
+to a registry is the deployment edge.
+
+## Decision
+
+1. **`serve`'s output becomes a static callable-resource card — the third ARD
+ archetype.** `--emit-ard` now emits three entry kinds: **source-package**
+ (existing; the design-time package feed a profile is fetched from to embed),
+ **precompiled-bundle** (existing; drop-in native artifact), and
+ **callable-resource card** (new; the fully-assembled invocation contract — exposed
+ capabilities per ADR-0015, their schemas, the instruction-package template, and
+ `derivedFrom` provenance). The card is static and deterministic; *answering* calls
+ is an optional thin host, not core.
+
+2. **You never author an MCP; MCP is a projection.** A capability projected *outbound*
+ across the boundary becomes a callable card (MCP tool / A2A card); a capability
+ required *inbound* is fulfilled by an external tool (ADR-0017, Door B). Both flatten
+ into "an MCP tool" on the wire; the skill/tool distinction is TypeFerence-internal.
+ MCP crosswalk (from ADR-0017): our `capability` → MCP **tool**; MCP **"capabilities"**
+ is transport-layer negotiation, unrelated to our `capability`.
+
+3. **`--emit-ard` defaults on when a publisher identity is configured.** ARD emission
+ needs a publisher domain, so it cannot hard-default; instead it is on whenever a
+ publisher identity is resolvable (flag or project config, e.g.
+ `typeference.trust.yaml`) and cleanly skipped with a hint otherwise. "The default
+ build output is catalog-ready" holds for projects that have declared who they are,
+ without failing a first-run `build` for a tire-kicker.
+
+4. **Publishing is an edge verb, not core.** The vocabulary chain is `build` → a
+ *catalog entry* → **`publish`** → a *registry*. Because pushing to a registry is
+ registry lifecycle (disclaimed by core), `publish` is an optional edge subcommand
+ (or an external registry tool consuming the emitted entry). Core *emits* the
+ registerable artifact deterministically; the network write is the edge. "Compile to
+ ARD as it exists" and "register to ARD as it exists" both **preserve the boundary**:
+ TypeFerence is a conformant *client* of an external standard, never the author of
+ registry lifecycle.
+
+## Consequences
+
+- The callable interface `serve` produced is now a first-class, static, registerable
+ artifact — exactly what an ARD-based agent finder needs (a card, not a live endpoint
+ to crawl).
+- Porting an MCP server to Go (expensive, runtime, off-thesis) is replaced by emitting
+ a card (cheap, deterministic, on-thesis).
+- `serve` the verb effectively evaporates into `--emit-ard`; a live host demotes to an
+ optional `host ` convenience that may be written later or never.
+- The determinism/core boundary is respected: emission is deterministic; publication
+ is an explicit edge.
+
+## Alternatives considered
+
+- **Keep `serve` as a runtime.** Off-thesis and the most expensive thing to port
+ (ADR-0014). Rejected in favor of emission.
+- **Put `publish` in the deterministic core.** Would pull registry lifecycle into core
+ semantics, which the design boundary disclaims. Rejected; `publish` is an edge.
+- **Emit an MCP-server config as an output format.** Conflates transport with the
+ packaging decision; the card is the right, transport-neutral layer (bindable to MCP
+ or A2A). Rejected.
diff --git a/docs/decisions/0019-context-lifecycles-two-doors.md b/docs/decisions/0019-context-lifecycles-two-doors.md
new file mode 100644
index 0000000..dff934c
--- /dev/null
+++ b/docs/decisions/0019-context-lifecycles-two-doors.md
@@ -0,0 +1,59 @@
+# 0019 — Context lifecycles: the two doors
+
+**Status:** Accepted (2026-07-23)
+
+## Context
+
+A knowledge vault (Obsidian or otherwise) relates to a TypeFerence agent in two
+distinct ways, and conflating them breaks either determinism or governance. The two
+sit on opposite sides of TypeFerence's most fundamental line — definition vs.
+deployment ("no deployment state, hosted runtime, or model credentials" in the
+definition layer).
+
+## Decision
+
+1. **Door A — compile-time context (baked in).** A context object (ADR-0013, an
+ instance of a `contextType`) exists as source and is compiled *into* the agent's
+ artifacts. Its content is part of the digest: reviewable, diffable, governed.
+ TypeFerence fully owns Door A — it reads the object and materializes it.
+
+2. **Door B — runtime context (queried live).** The compiled agent declares a
+ capability whose fulfillment is an external **tool** (ADR-0017); at execution the
+ *host* reads the live, mutable, possibly-private vault through that tool.
+ TypeFerence types only the contract — the `contextType` *shape* the data must
+ satisfy, the tool's signature, and the scope binding — and the content is **never**
+ in the digest. TypeFerence *describes* the access; it does not *perform* it. The
+ vault-reading tool body is **user-written** (the same boundary as "embeds no LLM
+ provider").
+
+3. **One vocabulary, two lifecycles.** The same `contextType` (e.g.
+ `castOfCharacters`) is the invariant *shape* whether baked (A) or queried (B). The
+ split is a *relationship* — "is this content in the definition, or queried by the
+ deployment?" — not necessarily a folder split; a single note's *shape* can be
+ authored at design-time (A) while its *content* is read at run-time (B).
+
+4. **What TypeFerence ships for Door B: the contract, never the connector.** No vault
+ reader, no Obsidian runtime, nothing to maintain. `obsidian-vault-lookup` and
+ friends are *user-written* tools conforming to a typed contract TypeFerence emits.
+
+## Consequences
+
+- Determinism is protected: runtime content is never baked into a deterministic
+ artifact, so it can't leak into or destabilize the digest.
+- Governance is protected: definitional content stays committed, reviewable, and
+ diffable rather than living as live external state.
+- Scope stays honest and small — TypeFerence types the *what* (shape, contract,
+ scope) and stays out of the *how* at runtime.
+- This is the lifecycle counterpart to the product framing: the vault is the informal
+ substrate; formalizing a slice *into* a definition is Door A, and typing a running
+ agent's *access* to the substrate is Door B.
+
+## Alternatives considered
+
+- **Treat the runtime vault like compile-time context (bake it in).** Bakes live,
+ mutable, possibly-private data into a deterministic artifact — kills determinism,
+ leaks data, couples the definition to transient state. Rejected.
+- **Treat compile-time context as a live resource.** Definitional content would live
+ as external state, losing reviewability and governance. Rejected.
+- **Ship a built-in vault/Obsidian connector.** Puts TypeFerence in the runtime
+ business it deliberately stays out of. Rejected; Door B tools are user-written.
diff --git a/docs/decisions/0020-close-the-source-language.md b/docs/decisions/0020-close-the-source-language.md
new file mode 100644
index 0000000..806d379
--- /dev/null
+++ b/docs/decisions/0020-close-the-source-language.md
@@ -0,0 +1,63 @@
+# 0020 — Close the source language and preserve modes
+
+**Status:** Accepted (2026-07-29)
+
+## Context
+
+ADRs 0012, 0013, 0016, 0017, and 0019 introduced invocation variants, typed
+context, abstract requirements, tools, and two context lifecycles. Their first
+implementation deliberately accepted several incomplete representations:
+
+- context types embedded a small, silently partial JSON Schema evaluator;
+- `contextFiles` remained a raw-path escape hatch;
+- resolved context values were not fully emitted;
+- variant requirements were flattened across modes;
+- tool validation proved declaration existence but did not preserve a runtime
+ import for later fulfillment;
+- a sealed abstract requirement was accepted even though no implementation
+ existed to protect.
+
+Those compromises prevent the type system from making a closed-world claim. They
+also make unsupported language appear accepted even when it has no consequence.
+
+## Decision
+
+Typed resources advance to `schemaVersion: 4`, a closed version that:
+
+1. replaces embedded JSON Schema for context sources with a small native type
+ language (`string`, `text`, scalar numerics/boolean, lists, maps, and named
+ context types);
+2. makes context instances first-class values with fully emitted fields and body;
+3. removes `contextFiles`; raw prose is an explicitly declared text-body context;
+4. preserves base and per-mode context/tool requirements rather than unioning
+ every mode;
+5. treats tools as runtime imports that build declares and link fulfills;
+6. rejects sealed bindings without a concrete implementation;
+7. distinguishes an omitted allow-list from an explicit empty allow-list so a
+ disjoint intersection cannot fail open.
+
+Unknown fields, schema constructors, and unsupported schema versions are errors.
+JSON Schema remains a generated interoperability projection, not the context
+source language.
+
+## Consequences
+
+- Version 4 can honestly claim that accepted context and tool declarations have
+ observable compile/link consequences.
+- Version 3 remains reproducible only through an explicit internal switch used
+ by the historical conformance corpus. Normal compiler, CLI, LSP, package, and
+ playground entrypoints reject it.
+- Target adapters must carry mode requirements and complete context values.
+- Authors wanting an opaque file must name that choice with a raw-text context
+ type.
+- This ADR supersedes the deferred/compatibility portions of ADRs 0012, 0013,
+ 0016, 0017, and 0019; their core object-model decisions remain accepted.
+
+## Alternatives considered
+
+- **Implement full JSON Schema.** Too large and semantically open for a language
+ whose purpose is predictable composition. Rejected for the source language.
+- **Keep `contextFiles` as an escape hatch.** It would keep a path-shaped hole in
+ every context policy. Rejected.
+- **Union all variant requirements.** Makes an optional mode constrain unrelated
+ targets and erases why variants exist. Rejected.
diff --git a/docs/decisions/0021-restore-locked-source-packages.md b/docs/decisions/0021-restore-locked-source-packages.md
new file mode 100644
index 0000000..976974a
--- /dev/null
+++ b/docs/decisions/0021-restore-locked-source-packages.md
@@ -0,0 +1,59 @@
+# 0021 — Restore exact, locked source packages
+
+**Status:** Accepted (2026-07-29)
+
+## Context
+
+Composition currently resolves only one source directory. An enterprise profile
+such as `marathon/enterprise-foundations` must be copied into each repository or
+left unresolved. Real organizations already publish internal artifacts through
+JFrog, Azure Artifacts, HTTP servers, and filesystem shares. TypeFerence needs to
+consume those systems without becoming another registry server or allowing feed
+state to leak into deterministic compilation.
+
+## Decision
+
+Add a phase before build:
+
+`restore source packages → build unlinked targets → link deployment → publish/run`
+
+The project manifest declares exact package dependencies. `typeference pack`
+creates a canonical content-addressed `.tferpkg`. `typeference restore` resolves
+the complete graph through externally configured namespace routes, verifies
+digests, writes a canonical committed `typeference.lock`, and materializes the
+locked tree under `obj/typeference/packages`.
+
+Once present, the lock is authoritative for `restore`; only `update` may
+re-resolve and rewrite it after the author changes exact manifest dependencies.
+
+The lock records identity, exact version, digest, exports, and dependency edges.
+It never records a feed URL, credential, machine path, or retrieval timestamp.
+Build is offline and frozen: it reads a supplied materialized package directory,
+re-verifies every locked digest, and never searches feeds.
+
+Initial providers are filesystem and generic HTTP. JFrog Generic is the HTTP
+layout with JFrog authentication support. Azure Artifacts uses an official
+artifact transport selected by the implementation. Feed routing is scoped and
+deterministic: one namespace maps to one feed, and no fallback search is allowed.
+
+Version 4 uses exact versions and rejects graph conflicts and cycles. Range
+solving is deferred.
+
+## Consequences
+
+- An enterprise can publish defaults to existing artifact infrastructure and a
+ consumer can restore the complete dependency tree with one command.
+- Builds remain reproducible and work without network access.
+- Changing staging/prod feed addresses does not change source identity.
+- The package client is intentionally small; TypeFerence does not host registries.
+- A global content-addressed cache may accelerate restore, while the build-facing
+ materialized tree remains project-local and inspectable.
+
+## Alternatives considered
+
+- **Resolve directly during build.** Makes builds network-dependent and feed-state
+ sensitive. Rejected.
+- **Commit vendored dependency trees.** Reproducible but operationally hostile and
+ duplicates package bytes. Supported as an external workflow, not the model.
+- **Implement a semantic-version solver immediately.** Adds complexity before the
+ identity and feed boundary are proven. Deferred.
diff --git a/docs/decisions/0022-build-link-and-source-identity.md b/docs/decisions/0022-build-link-and-source-identity.md
new file mode 100644
index 0000000..766e882
--- /dev/null
+++ b/docs/decisions/0022-build-link-and-source-identity.md
@@ -0,0 +1,62 @@
+# 0022 — Separate build, link, and source identity
+
+**Status:** Accepted (2026-07-29)
+
+## Context
+
+The project manifest briefly accepted `deployment.a2aBaseUrl` and
+`deployment.mcpCommand`. The Codex target then used the command to change generated
+configuration bytes. This violated the existing rule that deployment metadata
+does not participate in target compilation, and it made an environment endpoint
+part of the source package digest. Hashing the whole source directory after
+writing output compounded the problem by allowing generated artifacts to affect
+source identity.
+
+A bare token such as `${TYPEFERENCE_MCP_COMMAND}` would only move the ambiguity:
+Codex does not define that token as a portable command substitution mechanism, and
+an active config containing it is not a complete artifact.
+
+## Decision
+
+Identity is source; address is deployment.
+
+The project manifest advances to schema version 2 and contains only stable package
+identity, publisher identity, and exact source dependencies. Build produces
+deterministic unlinked target packages plus typed link requirements. It emits no
+active host configuration that depends on commands, endpoints, environment
+variables, or credentials.
+
+Deployment lives in an explicit external closed-schema file. Target adapters have
+two phases:
+
+```text
+Build(resolved definition) -> deterministic unlinked package
+Materialize(unlinked package, deployment bindings) -> runnable target
+```
+
+The linker validates selected modes, tool/provider bindings, endpoint syntax, and
+environment/secret references, then uses structural serializers for host-native
+configuration. It never changes the compiled semantics.
+
+Source identity hashes an explicit resource set and lockfile. It excludes output,
+restored packages, deployment/feed configuration, signature maps, VCS data, and
+unreferenced files. Target provenance records source and dependency digests.
+Linked provenance records the deployment digest separately.
+
+## Consequences
+
+- Staging and production share source and unlinked-target digests.
+- Codex, Copilot, Cursor, MCP, A2A, and future adapters can share one typed linker
+ boundary without pretending their runtime configuration formats are source.
+- Callable cards are emitted only when an endpoint/provider is explicitly linked.
+- Token replacement is available only as an explicit deployment-provider
+ projection; it is never implicit string substitution inside compiled files.
+
+## Alternatives considered
+
+- **Keep deployment in `typeference.yaml`.** Changes source identity for an
+ environmental address. Rejected.
+- **Bake CLI endpoint flags into build output.** Preserves manifest purity but
+ still makes build an environment-specific transform. Rejected.
+- **Emit unresolved tokens in active config.** Neither typed nor guaranteed to be
+ interpreted by the host. Rejected.
diff --git a/docs/decisions/README.md b/docs/decisions/README.md
index d4ecfd5..9869ccb 100644
--- a/docs/decisions/README.md
+++ b/docs/decisions/README.md
@@ -20,3 +20,14 @@ Format: `NNNN-short-title.md` with sections **Status**, **Context**, **Decision*
| [0009](0009-behavioral-equivalence-harness.md) | BETH: the behavioral equivalence test harness |
| [0010](0010-browser-playground.md) | Browser playground: the Go compiler as WebAssembly |
| [0011](0011-playground-live-runs.md) | Playground equivalence console: no secrets in the browser |
+| [0012](0012-invocation-mode-skill-variants.md) | Invocation-mode skill variants (Accepted) |
+| [0013](0013-user-defined-typed-context.md) | User-defined typed context (Accepted) |
+| [0014](0014-go-only-implementation.md) | Go-only implementation; the spec is the open invitation (Accepted) |
+| [0015](0015-exposure-and-visibility.md) | Exposure and visibility (Accepted) |
+| [0016](0016-sealing-mutability-presence.md) | Sealing: mutability and presence (Accepted) |
+| [0017](0017-tools-as-extern.md) | Tools as extern declarations (Accepted) |
+| [0018](0018-callable-resource-card-and-publishing.md) | Callable-resource card and publishing (Accepted) |
+| [0019](0019-context-lifecycles-two-doors.md) | Context lifecycles: the two doors (Accepted) |
+| [0020](0020-close-the-source-language.md) | Close the source language and preserve modes (Accepted) |
+| [0021](0021-restore-locked-source-packages.md) | Restore exact, locked source packages (Accepted) |
+| [0022](0022-build-link-and-source-identity.md) | Separate build, link, and source identity (Accepted) |
diff --git a/docs/release-checklist.md b/docs/release-checklist.md
index 877ea46..cafe5bb 100644
--- a/docs/release-checklist.md
+++ b/docs/release-checklist.md
@@ -8,7 +8,7 @@ version from the tag at build time via `-ldflags -X main.version`).
## Before tagging
1. On `main`, CI fully green: both test suites, the conformance suite
- (26/26 fixtures on both implementations), and the self-host drift gate.
+ (all committed fixtures), and the self-host drift gate.
2. `CHANGELOG.md`: move the `Unreleased` heading to the release date; confirm every
spec-affecting entry names its ADR.
3. `Directory.Build.props` `` matches the version being tagged.
@@ -41,7 +41,7 @@ reference output; and publishes a GitHub Release with per-platform archives and
## Versioning notes
- Tool releases (this checklist) version the CLIs and libraries. They do **not**
- version the source format: typed resources stay `schemaVersion: 3` and trust
+ version the source format: typed resources stay `schemaVersion: 4` and trust
configurations `schemaVersion: 1` until an incompatible format change, which
requires a specification change and an ADR first.
- Pre-1.0, breaking tool changes are allowed in any release but must be listed
diff --git a/docs/specification.md b/docs/specification.md
index 3914dec..c3f2f9f 100644
--- a/docs/specification.md
+++ b/docs/specification.md
@@ -1,147 +1,464 @@
# TypeFerence Draft Specification
-Status: experimental reference draft, July 2026. Typed resources use `schemaVersion: 3`; this document is not a claim of ecosystem-standard or production-stable status.
-
-## Scope and non-goals
-
-TypeFerence defines structural composition and deterministic compilation of agent instructions, capability contracts, skill implementations, and context references. It does not define an inference runtime, guarantee equivalent behavior across models or hosts, establish publisher trust, or provide resource discovery.
+Status: experimental reference draft, July 2026. Typed resources use
+`schemaVersion: 4`; project manifests use `schemaVersion: 2`. Version 4 is a
+deliberate closure of the source language. Unsupported fields and schema versions
+are errors rather than extension points.
+
+## Purpose and pipeline
+
+TypeFerence defines a closed, deterministic source language for composing agent
+definitions and compiling them into coherent target artifacts. It separates four
+operations:
+
+1. **restore** resolves declared source-package dependencies into a locked local
+ package tree;
+2. **build** resolves typed resources and emits deterministic, unlinked target
+ packages without network access or deployment state;
+3. **link** validates external deployment bindings and materializes runnable
+ host-native configuration;
+4. **publish/run** performs environment-specific side effects outside the
+ deterministic language core.
+
+The output of each earlier phase is complete input to the next. Build never
+implicitly restores. Link may fulfill declared runtime imports, but MUST NOT
+change instructions, contracts, context values, composition, selected
+implementations, or source identity.
+
+TypeFerence does not define an inference runtime, package registry server, secret
+manager, model credentials, external tool implementation, or deployment
+orchestrator.
+
+## Identity and addressing
+
+**Identity is source; address is deployment.** Resource IDs, package IDs,
+versions, publisher identity, dependency declarations, and the lock graph are
+source. Endpoints, commands, credentials, environment variables, host paths,
+registry addresses, and runtime process details are deployment.
+
+Deployment metadata MUST NOT occur in a source manifest and MUST NOT participate
+in resource embedding, composition, skill dispatch, target compilation, source
+package identity, or unlinked target identity. Feed URLs and credentials likewise
+identify where bytes can be retrieved, not what those bytes mean, and MUST NOT
+appear in a lockfile or source digest.
+
+## Project manifest
+
+A source root MAY contain `typeference.yaml`:
+
+```yaml
+schemaVersion: 2
+name: marathon/payments-agents
+version: 1.0.0
+publisher: marathon.example
+dependencies:
+ marathon/enterprise-foundations: 3.1.0
+```
-Agentic Resource Discovery (ARD) can advertise compiled TypeFerence outputs. ARD identifies and locates artifacts; TypeFerence produces target-specific artifacts before publication. Invocation remains the responsibility of MCP, A2A, OpenAPI, or a host-native mechanism.
+`name`, `version`, and every dependency version are required when the manifest is
+present. Versions are exact semantic versions in version 4; range solving is not
+defined. Dependency names use the resource namespace grammar. `publisher` is
+optional stable publication identity. Unknown fields, deployment fields, feed
+addresses, and credentials are errors.
-TypeFerence does not define ARD registry lifecycle, release policy, deprecation, deployment instance metadata, dependency manifests, auth feasibility hints, access or monetization policy, install-time consent envelopes, registry federation, DID resolution rules, relay addressing, search filters, registry APIs, or ARD governance. If ARD standardizes any of those fields, TypeFerence MAY preserve them as publication metadata, but they MUST NOT participate in TypeFerence embedding, composition, skill dispatch, or target compilation semantics.
+## Resource identity and kinds
-## Resource identity
+A compilation unit contains UTF-8 YAML or frontmatter-plus-body documents with
+`schemaVersion: 4`, a `kind`, and an `id`. IDs use
+`namespace/name@semantic-version`. Supported kinds are:
-A source tree contains YAML documents with `schemaVersion: 3`, a `kind`, and an `id`. IDs use `namespace/name@semantic-version`. Supported kinds are `agent`, `profile`, `interface`, `capability`, and `skill`.
+- `agent`: a concrete, target-emitting composition;
+- `profile`: a reusable, possibly abstract composition;
+- `interface`: a structural contract over slots and capabilities;
+- `capability`: a stable public method contract;
+- `skill`: a model-fulfilled capability implementation;
+- `tool`: a declared runtime import fulfilled at link time;
+- `contextType`: a named structural type for context values;
+- `context`: a compile-time value of a context type.
-## Agents and profiles
+All references are exact resource IDs. A reference resolves either to a document
+in the root package or to an export in the committed dependency graph. Searching
+feeds, selecting a similarly named resource, or allowing a local resource to
+shadow a dependency is forbidden.
-An agent is an identity-bearing unit that MAY produce target artifacts. A profile is a reusable composition unit for organizational, domain, or team defaults. Agents MAY embed profiles or agents. Profiles MAY embed profiles but MUST NOT embed agents. Embedding promotes the embedded resources' slots, norms, contexts, and capability bindings into the embedding resource. An embedding graph MUST NOT contain a cycle. No universal root is required.
+## Composition
-Profiles participate in composition and validation but do not produce target bundles. This lets users start with `kind: agent` while platform teams define reusable profiles underneath.
+Agents MAY embed profiles or agents. Profiles MAY embed profiles but MUST NOT
+embed agents. An embedding graph MUST NOT contain a cycle.
Resolution proceeds from embedded resources toward the embedding resource:
-1. Display name and description belong to the declaring resource and are not promoted.
-2. Slots are promoted by name. The shallowest declaration wins; conflicting declarations at the same depth are ambiguous unless the embedding resource declares that slot locally.
-3. Norms and context paths append in embedding order and deduplicate in first-seen order.
-4. Capability bindings are promoted by capability ID. The shallowest implementation wins; conflicting implementations at the same depth are ambiguous unless the embedding resource binds that capability locally.
-5. Interfaces are computed structurally from the final promoted member set.
-
-Every resolved contribution records its source resource in provenance.
-
-## Interfaces
-
-Interfaces are contracts only. They MAY require slot names and capability IDs, and MAY embed other interfaces. They MUST NOT provide implementation. Every agent whose resolved slots and capability bindings contain all requirements satisfies the interface implicitly; agents do not declare `implements`. Interface embedding MUST NOT contain a cycle.
-
-## Capabilities and skill implementations
-
-A capability defines a stable semantic method slot and its public JSON input/output schemas. It has no instructions and no runtime context.
-
-A skill defines instructions, conditional context references, and JSON input/output schemas. Every skill MUST declare `binds: `. A skill implementation MUST preserve the bound capability's canonical input and output schemas. It MAY change instructions, description, and conditional context.
-
-An agent's or profile's `skills` list binds skill implementations into that resource's resolved method set. If a binding omits `capability`, the capability is inferred from the referenced skill's `binds` field. If it names `capability`, that value MUST match the referenced skill's `binds` field. The outer dispatch name resolves the capability to the nearest compatible skill implementation while the embedded resource retains its own namespace.
-
-## Dispatch
-
-Concrete skills are exposed as `agent-name.skill-name`. Tool names are unique within the TypeFerence MCP server. A call validates required and unknown top-level properties, then returns an invocation package:
-
-```json
-{
- "agentId": "helio/payments-repo-agent@1.0.0",
- "skillId": "helio/skills/payments-repository-status@1.0.0",
- "dispatchName": "payments-repo-agent.repository-status",
- "arguments": { "focus": "release" },
- "instructions": "...",
- "contextReferences": ["context/payments-service.md"],
- "targetHints": { "codex": ".agents/skills" },
- "provenance": []
-}
+1. Display name and description belong to their declaring resource.
+2. Slots promote by name. The shallowest declaration wins; different
+ declarations at the same depth are ambiguous unless declared locally.
+3. Norms and held context IDs append in embedding order and deduplicate in
+ first-seen order.
+4. Capability bindings promote by capability ID. The shallowest compatible
+ implementation wins; different implementations at the same depth are
+ ambiguous unless bound locally.
+5. Allow-lists intersect. A disjoint intersection is an explicit empty set, not
+ an unrestricted set.
+6. Every contribution records source-resource provenance.
+
+Profiles may retain abstract required capability bindings. Agents are concrete
+and MUST fulfill every promoted requirement.
+
+## Interfaces, capabilities, skills, and bindings
+
+Interfaces MAY require typed slots and capability IDs and MAY embed interfaces.
+They provide no implementation. In version 4, `requiresSlots` maps each required
+slot name to a contextType ID:
+
+```yaml
+requiresSlots:
+ repository: acme/context-types/repository-evidence@1.0.0
```
-Hosts execute the package. The v1 server does not call an LLM.
-
-## Deterministic compilation
-
-Compilers MUST normalize paths to forward slashes, use LF newlines, omit timestamps, and sort resources by stable ID. Repeated builds from identical source MUST be byte-identical, and independent conforming implementations MUST produce byte-identical artifacts from identical source (see the conformance suite under `conformance/`).
-
-### Canonical text and ordering
-
-- All source and artifact files are UTF-8 text. Implementations MUST ignore a leading byte-order mark when reading and MUST write artifacts as UTF-8 without a byte-order mark. Behavior on invalid UTF-8 input is unspecified; implementations MAY reject it.
-- Wherever this specification requires sorting or deduplicating strings, the ordering is lexicographic by Unicode code point (equivalently: by UTF-8 byte sequence). Implementations MUST NOT use UTF-16 code-unit order, culture-aware collation, or case folding. To keep the divergent encodings' orderings observably identical, canonical key spaces are restricted to ASCII: resource IDs (already ASCII by grammar), slot names, and trust metadata keys MUST match `^[A-Za-z0-9][A-Za-z0-9._-]*$`, and trust identity URIs MUST be ASCII (internationalized authorities MUST be pre-encoded as punycode).
-- Emitted artifact files always end with LF-terminated content exactly as specified per artifact; no implementation-chosen trailing whitespace is permitted.
-
-### Canonical JSON serialization
-
-JSON artifacts (`bundle.json`, `provenance.json`, `ai-catalog.json`) are serialized as follows:
-
-- **Member order.** Objects serialize their members in the canonical order defined per artifact shape (for `bundle.json`: `id`, `displayName`, `description`, `emit`, `embeds`, `satisfies`, `slots`, `workingNorms`, `contextFiles`, `skills`, `provenance`; skills: `dispatchName`, `capabilityId`, `implementationId`, `description`, `instructions`, `inputSchema`, `outputSchema`, `contextFiles`, `provenance`; provenance entries: `field`, `source`). Map-like objects (slots, trust manifests, trust metadata) serialize keys in canonical string order as defined above. Member order is not alphabetical unless stated.
-- **Layout.** Indented artifacts use two-space indentation, `": "` after keys, one member or element per line, `[]` and `{}` for empty collections, and LF line endings, followed by one trailing LF at end of file. Canonical embedded JSON (schema strings) uses the compact layout with no whitespace.
-- **String escaping.** ASCII characters 0x20–0x7E are emitted literally except `"` `&` `'` `+` `<` `>` `` ` `` and `\`. Backspace, tab, line feed, form feed, and carriage return use the two-character escapes `\b` `\t` `\n` `\f` `\r`; backslash is `\\`. Every other character — remaining control characters and all code points above 0x7E — is escaped as `\uXXXX` with uppercase hexadecimal digits, using UTF-16 surrogate pairs for supplementary-plane code points.
-- **Numbers.** JSON number tokens carried through canonicalization (for example inside capability schemas) are preserved byte-for-byte as authored; implementations MUST NOT reformat `1.0` as `1` or normalize exponent notation.
-- **Schema canonicalization.** A JSON schema's canonical form is its compact serialization under the rules above, preserving authored member order, duplicate keys, and number tokens. Schema equality (capability contract preservation) is byte equality of canonical forms.
-
-The neutral target emits `AGENTS.md`, `bundle.json`, `provenance.json`, and skill folders. Codex, GitHub Copilot, and Cursor adapters emit their native instruction and skill/rule locations. Target-specific outputs MAY add native metadata. An adapter MUST represent each portable resolved field or emit a diagnostic when the target cannot represent it; this requirement does not imply equivalent model behavior.
-
-Behavioral equivalence across hosts is a long-term conformance objective, not a v1 compiler guarantee. Claims of equivalence MUST be supported by target-specific evaluations over declared scenarios and acceptance criteria.
-
-## ARD publication
+The supplied slot value MUST reference a context resource satisfying that type
+or a refinement. Satisfaction is inferred from the resolved member set.
-ARD publication is an optional post-compilation operation. It MUST NOT be modeled as a peer compilation target. This section tracks the ARD v0.9 draft proposal (see `docs/ard-alignment.md` for the review baseline); draft evolution MAY require corresponding changes in a future TypeFerence schema version.
+A capability defines canonical JSON `inputSchema` and `outputSchema`. A skill
+declares `binds: ` and MUST preserve those schemas byte-for-byte
+after canonicalization. Its instructions provide the model implementation.
-When requested, the reference compiler emits:
+Bindings have independent presence and mutability axes:
-1. One canonical source-package catalog entry containing the complete TypeFerence source tree and its SHA-256 digest.
-2. One target-bundle entry for every concrete agent and selected compilation target.
-3. `derivedFrom` provenance from each target bundle to the canonical source identifier and digest.
-
-The v1 package media types are experimental `application/vnd.typeference.source-package+json` and `application/vnd.typeference.target-bundle+json`. A target bundle contains the exact generated files and names its intended runtime. ARD discovery does not install those files or make one target's format executable by another target. Directly callable services SHOULD instead be published using their native MCP, A2A, OpenAPI, or successor artifact card after deployment.
-
-TypeFerence-generated catalog entries intentionally omit ARD-owned lifecycle, deployment, dependency, install-safety, federation, and registry-search metadata unless supplied as external publication metadata. TypeFerence source versions describe authoring resources; they do not imply discovery-time availability, migration windows, deprecation state, supported regions, credential requirements, commercial terms, or registry federation consent.
-
-### Trust metadata compilation
-
-TypeFerence targets the draft AI Catalog Trust Manifest as published at . Draft evolution MAY require corresponding changes in a future TypeFerence schema version.
-
-A source root MAY contain `typeference.trust.yaml`. The file is part of the canonical source package and its digest, but it is not a typed agent resource and does not participate in embedding or behavioral resolution. A different trust configuration beneath the source root MAY be selected explicitly. Trust metadata is publication configuration: native target bundles remain usable without ARD.
-
-The trust configuration has `schemaVersion: 1` and MAY contain `source` and `bundles` profiles. At least one profile is required. A source profile requires a literal `identity`. A bundles profile requires an `identityTemplate` containing both `{agent}` and `{target}`; `{publisher}` and `{version}` are also supported. Each profile MAY contain:
-
-- `identityType`
-- an AI Catalog `trustSchema`
-- AI Catalog `attestations`
-- additional AI Catalog `provenance` links
-- arbitrary JSON-compatible `metadata`
-- `signatureIntent` containing an algorithm, key reference, and optional required flag
-
-TypeFerence MUST preserve its generated `derivedFrom` link as the first provenance link for every target bundle. Configured links follow it in source order. It MUST add a deterministic target artifact digest to `com.github.buchk.typeference.artifactDigest` in Trust Manifest metadata. The digest is covered when the Trust Manifest is externally signed.
-
-Trust metadata is declarative. TypeFerence MUST NOT dereference identity, attestation, policy, provenance, or key URIs; issue compliance claims; infer a SLSA level; claim that runtime governance executed; or treat referenced TRACE-style runtime evidence as deployment state. A referenced attestation asserts only that the publisher supplied that reference.
-
-Identity and URI syntax, known publisher-domain bindings, attestation shape, digest encoding, template placeholders, and metadata keys MUST be validated locally. Identity schemes remain open, but known `did`, `spiffe`, and `https` scheme/type contradictions MUST be rejected. Digests MUST be lowercase SHA-256, SHA-384, or SHA-512 values in `algorithm:hex` form.
-
-### Artifact digest algorithm
-
-`typeference-directory-v1` hashes a text artifact directory as follows:
-
-1. Enumerate files recursively and sort their forward-slash relative paths in canonical string order (Unicode code point order; see Deterministic compilation). Sorting platform-native paths is non-conforming: platform separators (`\` vs `/`) order differently against other characters, which makes the digest platform-dependent.
-2. For each file, append its forward-slash relative path, one NUL byte, its UTF-8 text content with any leading byte-order mark removed and CRLF normalized to LF, and one NUL byte.
-3. SHA-256 hash the UTF-8 encoding of the resulting sequence and encode it as lowercase hexadecimal with a `sha256:` prefix.
-
-The v1 package formats contain text files only. A future binary package format MUST define a different digest scheme rather than silently changing this algorithm.
-
-### Trust Manifest signatures
-
-The AI Catalog `signature` member is reserved for a compact detached JWS over the RFC 8785 JCS-canonicalized Trust Manifest after removing `signature`. TypeFerence MUST NOT place placeholders in this field.
-
-TypeFerence MAY import externally created detached JWS strings from a JSON object keyed by generated catalog identifier. It validates compact detached form but does not verify cryptographic validity or resolve keys. Unknown identifiers MUST be rejected. When `signatureIntent.required` is true, publication MUST fail unless the entry has an imported signature. An explicit unsigned-staging option MAY bypass this check solely to emit the payload for an external signer. Signing intent is emitted under `com.github.buchk.typeference.signatureIntent` with invariant `status: external`; whether signing is fulfilled is represented solely by the standard `signature` member. This metadata MUST NOT change when a signature is injected, because it is part of the signed payload.
+```yaml
+skills:
+ - capability: acme/capabilities/audit@1.0.0
+ required: true
+ - capability: acme/capabilities/safety@1.0.0
+ ref: acme/skills/safety@1.0.0
+ required: true
+ sealed: true
+```
-The signature map MUST reside outside the source root. This prevents a cycle in which adding a signature changes the source-package digest embedded in the content being signed. Repeated builds from identical source, trust configuration, and signature map MUST be byte-identical.
+A binding without `ref` MUST be `required: true` and MUST NOT be sealed. Sealing
+protects a supplied implementation; it cannot protect an absent one. Therefore
+`required: true, sealed: true` without `ref` is a compile error. A concrete
+required-and-sealed binding is valid. Rebinding or suppressing a promoted sealed
+binding is an error.
+
+## Invocation modes
+
+A skill declares either `instructions` or `variants`, never both. Base
+requirements apply to every mode; variant requirements are additive:
+
+```yaml
+kind: skill
+requiresTools: [acme/tools/repository@1.0.0]
+variants:
+ manual:
+ instructions: Explain the evidence.
+ pipeline:
+ instructions: Emit strict JSON.
+ requiresTools: [acme/tools/build-signals@1.0.0]
+```
-## Diff contract
+For mode `m`, effective requirements are `base ∪ variant[m]`. Compilation MUST
+preserve the mapping and MUST NOT flatten all variants into one universal
+requirement set. A target build may carry every mode. Link selects or validates
+the modes requested by deployment and fails if any selected mode's requirements
+are unfulfilled. A target adapter MAY declare a default mode, but MUST diagnose a
+missing default rather than silently substitute another mode.
+
+Variants may change instructions and add context/tool requirements. They MUST NOT
+change the bound capability or its schemas.
+
+## Native context type language
+
+Context types use the TypeFerence type language, not embedded JSON Schema:
+
+```yaml
+schemaVersion: 4
+kind: contextType
+id: acme/context-types/team@1.0.0
+fields:
+ owner:
+ type: string
+ required: true
+ participants:
+ type:
+ list: string
+ required: true
+ governed:
+ type: boolean
+ default: false
+ attributes:
+ type:
+ map: string
+body:
+ type: text
+ required: false
+```
-`typeference diff` compiles to temporary storage and compares relative paths and content. Exit code `0` means identical, `1` means changed, and `2` means validation or execution failed.
+Version 4 supports `string`, `text`, `boolean`, `integer`, `number`, `list`,
+`map`, and exact references to named context types. A field may be `required`
+and may declare a type-correct scalar or collection `default`. Unknown
+constructors and unsupported constraints such as unions, `oneOf`, regular
+expressions, arbitrary `$ref`, or open properties are errors.
+Named value references are acyclic and may target field-only context types;
+a context type with a required prose body cannot be embedded as an inline field
+value.
+
+A context instance declares its type and values:
+
+```yaml
+schemaVersion: 4
+kind: context
+id: acme/context/payments-team@1.0.0
+contextType: acme/context-types/team@1.0.0
+values:
+ owner: payments-platform
+ participants: [Ari, Sam]
+---
+Stable prose carried as the typed body.
+```
-## Security
+Values are parsed schema-directed; YAML implicit scalar typing MUST NOT decide
+their meaning. Missing required fields, unknown fields, wrong shapes, and invalid
+named-type references are errors. Defaults are materialized before hashing and
+emission. Resolved artifacts preserve the complete canonical value object and
+body, so any semantic context change changes target bytes.
+
+Context types MAY embed other context types to refine them. Field redeclaration
+must preserve the original type and may only strengthen optional to required.
+A context satisfies its declared type and every embedded base type.
+
+Agents and profiles hold context by resource ID using `context`. Skills require
+types using `requiresContextTypes`. `allowedContextTypes` is a closed whitelist;
+omitting it means unrestricted and specifying `[]` means no context is allowed.
+Allow-lists intersect through composition without treating the empty result as
+unrestricted.
+
+`contextFiles` does not exist in version 4. Raw prose is represented honestly as
+a context type with a required `text` body. Slot values that carry context MUST
+reference context resource IDs, never filesystem paths. All referenced files and
+resources MUST be explicit members of the source package and resolve beneath its
+root.
+
+JSON Schema MAY be emitted as a target interoperability projection of a native
+context type. It is not accepted as the source language and never controls
+TypeFerence validation semantics.
+
+## Tools as runtime imports
+
+A `tool` is an extern declaration: a versioned runtime import with canonical
+input/output schemas and optional non-secret requirement metadata. It contains no
+implementation, command, endpoint, environment-specific scope, or credential
+value.
+
+A skill or variant lists exact tool IDs in `requiresTools`. Build verifies that
+each declaration exists, is a `tool`, and is structurally valid. It does not
+pretend the runtime implementation exists. Link binds each effective required
+tool to one compatible provider in the deployment file and fails closed if
+missing or incompatible.
+
+The vocabulary is intentionally separate:
+
+- capability: exported semantic contract;
+- skill: model implementation of a capability;
+- tool: imported runtime callable used by a skill.
+
+An MCP projection may map capabilities and tools to MCP wire-level tools, but
+that transport projection does not collapse the source-language categories.
+
+## Packages, restore, and lockfiles
+
+`typeference pack` emits one canonical source package (`.tferpkg`) containing:
+
+- package name and exact version;
+- exact dependency declarations;
+- sorted exported resource IDs;
+- sorted canonical source paths and normalized UTF-8 contents;
+- a `sha256:` package digest.
+
+The package envelope is canonical JSON. Paths use `/`, may not be absolute,
+contain `..`, or collide after normalization. Files are LF-normalized and BOM-free.
+No archive timestamp, host path, feed address, credential, deployment value,
+generated target, or VCS data is permitted.
+
+`typeference restore` reads the manifest, resolves the exact dependency graph from
+configured routes, verifies every package digest, rejects cycles/conflicts, writes
+`typeference.lock`, and materializes the complete graph beneath
+`obj/typeference/packages`. If a lockfile already exists, `restore` honors it and
+MUST reject any manifest/lock/feed disagreement without rewriting it;
+`restore --locked` additionally requires the lockfile to exist. `typeference
+update` is the explicit operation that re-resolves and rewrites the graph after
+the author changes exact manifest dependencies.
+
+The canonical lockfile records root manifest identity, package name/version,
+digest, exports, and dependency edges in deterministic order. It MUST NOT contain
+feed URLs, credentials, cache paths, retrieval timestamps, or mutable tags.
+
+Feed configuration is external. Implementations SHOULD support scoped routes and
+MAY implement filesystem, generic HTTP/JFrog Generic, and Azure Artifacts
+transports. Routes select one feed for a namespace; implementations MUST NOT
+search every configured feed. Credentials are environment/helper references and
+never source.
+
+Build reads only the source root, lockfile, and a supplied materialized package
+directory. It verifies locked digests and performs no network access. Missing,
+undeclared, conflicting, or corrupt dependencies are errors.
+
+## Source membership and digests
+
+The source digest hashes an explicit resource set, never an arbitrary recursive
+directory after output has been written. It includes:
+
+- the project manifest and lockfile when present;
+- every root resource and context source file accepted by the loader;
+- explicitly selected trust configuration;
+- referenced source assets defined by a future schema version.
+
+It excludes `.git`, `dist`, `bin`, `obj`, restored packages, deployment files,
+feed configuration, signature maps, generated artifacts, and unreferenced files.
+Build MUST reject an output directory nested inside the source root unless the
+output path is in a normatively excluded generated directory. The source root
+itself is never a valid output directory.
+
+`typeference-resource-set-v1` sorts normalized source-relative paths by UTF-8 byte
+order and hashes, for each file, `path`, NUL, normalized content, NUL using
+SHA-256. Target provenance records the root source digest, the ordered locked
+dependency digests, and target adapter identity. Release metadata belongs to the
+distributed compiler binary, not the reproducible source-derived artifact.
+
+`typeference-directory-v1` remains the target-directory digest: recursively sort
+forward-slash paths, then hash `path`, NUL, normalized text, NUL. It applies only
+to already-defined artifact directories, not source identity.
+
+## Build targets
+
+Build emits deterministic **unlinked** packages for neutral, Codex, GitHub
+Copilot, and Cursor targets. Each adapter MUST represent every portable resolved
+field or emit a diagnostic. Host-native active runtime configuration is not a
+build artifact.
+
+Every emitted agent artifact path and callable dispatch name MUST be unique
+after target-native naming. If two otherwise distinct resource IDs collapse to
+the same target name, compilation fails rather than overwriting either artifact.
+
+An unlinked target contains instructions, skills, native rules, bundle metadata,
+mode requirements, tool imports, complete compiled context values, provenance,
+and a link-requirements manifest. It MUST NOT contain invented endpoints,
+commands, unresolved `${TOKEN}` text in active configuration, or environment
+defaults.
+
+## Deployment files and link
+
+Deployment is supplied explicitly outside the source package:
+
+```yaml
+schemaVersion: 1
+environment: staging
+artifacts:
+ marathon/agents/payments@1.0.0:
+ modes: [manual, a2a]
+providers:
+ payments-tools:
+ kind: mcp
+ transport: stdio
+ command: payments-mcp-server
+ args: [--bundle, "{bundle}"]
+ environment:
+ PAYMENTS_TOKEN:
+ fromEnvironment: PAYMENTS_TOKEN
+toolBindings:
+ marathon/tools/repository-signals@1.0.0:
+ provider: payments-tools
+ remoteName: repository_signals
+agentEndpoints:
+ marathon/agents/payments@1.0.0:
+ a2aUrl: https://agents.example/payments
+```
-All source references MUST resolve beneath the source root and MUST exist. MCP stdio logging MUST avoid stdout. Tool annotations and generated instructions are descriptive, not authorization. Hosts remain responsible for access control and user approval.
+The deployment schema is closed. Commands and arguments are distinct string
+values. Stdio environment entries are same-name forwarding references
+(`NAME: { fromEnvironment: NAME }`), never secret values. HTTP bearer
+authentication uses `bearerTokenEnvironment` and likewise names an environment
+variable rather than its value. URLs MUST be absolute HTTPS URLs unless a
+transport-specific local-development option explicitly allows otherwise.
+
+`typeference link --deployment --out `:
+
+1. verifies the unlinked target digest;
+2. selects the artifact modes;
+3. validates every effective tool import and endpoint requirement;
+4. emits a canonical binding manifest that preserves each source tool ID,
+ selected mode, provider, and provider-level remote name;
+5. structurally serializes host-native configuration;
+6. records deployment-file and materialized-artifact digests separately.
+
+For Codex, local stdio MCP bindings materialize `.codex/config.toml` using TOML
+string escaping; HTTP bindings use the native URL shape. Build never emits an
+active Codex MCP server entry. Equivalent target-native materialization applies
+to other adapters. Hand-concatenating unescaped deployment values is forbidden.
+
+An adapter that builds one concrete mode MUST require link to select exactly
+that mode. The initial Codex, Copilot, and Cursor adapters materialize `manual`
+for multimodal skills; they reject a deployment that claims to select another
+mode. A future adapter may carry multiple renderings and select one during link,
+but it MUST NOT validate one mode's tools while installing another mode's
+instructions.
+
+A2A Agent Cards are linked from the neutral artifact. For a multimodal agent,
+the deployment MUST select `a2a`; only exposed capabilities whose concrete skill
+is unimodal or defines an `a2a` variant are advertised.
+
+The same source and unlinked target linked for staging and production MUST retain
+identical source and unlinked-target digests. Only the linked artifact and
+deployment provenance may differ.
+
+Linked output preserves the input integrity index as historical
+`unlinked-build.json`; it MUST NOT leave that index named as though it described
+the mutated linked tree. Link provenance records a digest for each linked agent
+directory from outside that directory, avoiding a self-digest cycle.
+
+## ARD and callable publication
+
+ARD publication consumes build and optional link artifacts. Source-package and
+unlinked-target entries are deterministic. A2A Agent Cards, MCP server cards, and
+other callable publications require an explicitly linked endpoint/provider;
+TypeFerence MUST NOT invent an address from publisher identity.
+
+Publisher identity may participate in stable URNs and trust identity. Endpoint
+addresses do not. Publication remains an explicit edge operation and may perform
+network I/O only when requested.
+
+## Trust metadata
+
+A source root MAY contain `typeference.trust.yaml` or select one explicitly. Trust
+metadata is declarative and participates in source-package publication, not
+behavioral resolution. TypeFerence MUST NOT dereference identity, attestation,
+provenance, policy, or key URIs; sign; verify cryptographic validity; or resolve
+keys.
+
+Externally produced compact detached JWS strings MAY be imported from a signature
+map outside the source root. Unknown identifiers are errors.
+`signatureIntent.required` fails closed unless an explicit unsigned-staging option
+is used solely to emit payloads for an external signer. The signature map MUST
+remain outside the source root to avoid a digest/signature cycle.
+
+## Canonicalization
+
+All accepted text is UTF-8, BOM-free after loading, LF-normalized, and emitted with
+one final LF when its artifact shape requires text termination. Paths use `/`.
+Stable string ordering is lexicographic by UTF-8 bytes. Resource IDs, mode names,
+slot names, field names, and metadata keys use restricted ASCII grammars.
+
+Canonical JSON preserves schema number tokens and authored schema member order,
+uses defined artifact member order, two-space indentation for files, compact form
+for embedded schemas, and deterministic escaping. Map-like objects sort keys.
+Repeated restore, build, pack, and link operations over identical respective
+inputs MUST be byte-identical on every platform.
+
+## Diff and security
+
+`typeference diff` compares relative paths and normalized content. Exit code `0`
+means identical, `1` changed, and `2` validation/execution failure.
+
+References MUST resolve beneath an authorized package root. Package extraction
+MUST prevent path traversal and overwrite outside its materialization directory.
+Logs MUST avoid secrets and MCP stdio stdout. Tool annotations and generated
+instructions are descriptive, not authorization; hosts remain responsible for
+access control and user approval.
diff --git a/docs/whitepaper.md b/docs/whitepaper.md
index c4bc25d..dfa8a2b 100644
--- a/docs/whitepaper.md
+++ b/docs/whitepaper.md
@@ -8,7 +8,7 @@ TypeFerence contributors - July 2026
Organizations are teaching AI assistants the same business rules repeatedly: once for a coding agent, again for an executive assistant, again for each repository, and again for every vendor-specific Markdown format. The result is semantic drift hidden inside apparently simple files.
-TypeFerence treats agent definitions as typed source code. Organizations define reusable profiles, structurally satisfied interfaces, versioned capabilities, and skill implementations, then combine behavior through Go-like embedding. A deterministic compiler resolves those definitions and emits native artifacts for Codex, GitHub Copilot, Cursor, neutral Agent Markdown, and MCP. The central result is not merely portability. It is coherent reuse of domain decisions across people, repositories, tools, and time. Behavioral equivalence across hosts is the long-term objective; v3 supplies a typed baseline from which equivalence can be evaluated rather than claiming it has already been achieved.
+TypeFerence treats agent definitions as typed source code. Organizations define reusable profiles, structurally satisfied interfaces, versioned capabilities, and skill implementations, then combine behavior through Go-like embedding. A deterministic compiler resolves those definitions and emits unlinked native artifacts for Codex, GitHub Copilot, Cursor, and neutral Agent Markdown. Runtime MCP/A2A configuration is linked from explicit deployment bindings rather than invented during compilation. The central result is not merely portability. It is coherent reuse of domain decisions across people, repositories, tools, and time. Behavioral equivalence across hosts is the long-term objective; v4 supplies a closed typed baseline from which equivalence can be evaluated rather than claiming it has already been achieved.
## 1. The coherence problem
@@ -18,7 +18,7 @@ This is the answer to "why not just write `AGENTS.md` directly?" Direct instruct
The underlying problem is repeated domain modeling. Each local agent solves identity, capability, context selection, and governance again. Vendor portability is one visible symptom; duplicated organizational reasoning is the larger cost.
-TypeFerence introduces a canonical typed layer above runtime Markdown. Source definitions are small. Skills conditionally reference context. Compilation is deterministic. The generated artifacts remain ordinary files that existing tools understand.
+TypeFerence introduces a canonical typed layer above runtime Markdown. Source definitions are small. Context is a first-class typed value, skills declare the context types and runtime tools they require, and compilation is deterministic. The generated artifacts remain ordinary files that existing tools understand.
## 2. Composition over ancestry
@@ -44,7 +44,7 @@ The compiler parses resources, validates references, resolves profile and agent

-The neutral output includes resolved instructions, bundles, skill packages, and provenance. Codex receives `AGENTS.md`, open skill folders, and MCP configuration. Copilot receives repository instructions and agent profiles. Cursor receives `AGENTS.md` and project rules. Adapters may use native capabilities without forcing every platform into a lowest-common-denominator file, and must surface diagnostics when a target cannot represent a portable field.
+The neutral output includes resolved instructions, bundles, skill packages, link requirements, and provenance. Codex receives `AGENTS.md` and open skill folders; its active MCP configuration is emitted only by the deployment linker. Copilot receives repository instructions and agent profiles. Cursor receives `AGENTS.md` and project rules. Adapters may use native capabilities without forcing every platform into a lowest-common-denominator file, and must surface diagnostics when a target cannot represent a portable field.
Stable sorting, normalized paths, LF newlines, canonical JSON, and the absence of timestamps make builds byte-for-byte reproducible. A source change therefore yields a reviewable artifact diff.
@@ -58,25 +58,25 @@ TypeFerence occupies the preceding layer. It resolves one governed source defini
ARD can tell a client that a Copilot agent artifact exists; it does not transform that artifact into Cursor rules. TypeFerence performs the target transformation. Discovery interoperability, definition portability, and behavioral equivalence are separate properties.
-The publication unit is therefore a matrix. A publisher may advertise one canonical TypeFerence source package for audit and rebuilding, plus separately versioned Codex, Copilot, Cursor, neutral, MCP, or future target variants. Every compiled entry points back to the canonical source identifier and digest. Consumers normally select a prebuilt artifact for their runtime rather than compiling untrusted source during invocation.
+The publication unit is therefore a matrix. A publisher may advertise one canonical TypeFerence source package for audit and rebuilding, plus separately versioned Codex, Copilot, Cursor, neutral, or future target variants and explicitly linked callable publications. Every compiled entry points back to the canonical source identifier and digest. Consumers normally select a prebuilt artifact for their runtime rather than compiling untrusted source during invocation.
Static host configurations are installable artifacts, not remotely callable agents. An ARD entry can carry or locate the bundle, but a target-aware consumer still has to install it. A deployed MCP or A2A service can instead be advertised using its native server or agent card and invoked through that protocol. The prototype's TypeFerence package media types are experimental until a broader packaging contract exists.
-## 6. Skills, context, and dispatch
+## 6. Skills, context, and runtime imports
-Large prompts are not required. A skill contains a concise description for discovery, its focused instructions, input/output schemas, and only the context references needed when invoked. The host receives an invocation package and loads that context at execution time.
+A context value is declared by ID against a small native context type and compiled into artifacts with provenance. There is no raw file-path escape hatch. A skill contains focused instructions and canonical input/output schemas, and declares the context types and external tools it needs. Base requirements apply to every invocation mode; variant requirements remain attached to their mode.
-MCP provides the runtime object boundary. Each concrete method is exposed as `agent-name.skill-name`. Calling `payments-repo-agent.repository-status` resolves the repository-status capability to the payments implementation, validates arguments, and returns instructions, context references, target hints, and provenance.
+Tools are runtime imports rather than hopeful names. Build verifies declarations and preserves imports in link requirements. Link selects modes and binds each effective import to an explicit provider such as an MCP stdio process or HTTPS server. TypeFerence never implements or executes the tool body.
-
+
-TypeFerence intentionally does not select a model or execute an agent turn. It compiles and dispatches coherent definitions; the host remains responsible for inference, permissions, and user interaction.
+TypeFerence intentionally does not select a model, execute an agent turn, or dispatch a tool call. It compiles coherent definitions and links declared imports to explicit deployment providers; the host remains responsible for inference, permissions, execution, and user interaction.
## 7. Agents beyond repositories
Engineering teams are plausible early adopters because their work is already versioned and reviewable, but the model is not repository-specific. The Helio example includes generic person and repository profiles, an executive assistant, and a specialized payments repository agent.
-The executive assistant can prepare a decision brief. When repository evidence is material, its skill requests the specialized repository-status method. The repository-facing agent returns an invocation package grounded in its own domain context. The person profile contributes communication behavior without duplicating repository knowledge.
+The executive assistant can prepare a decision brief. When repository evidence is material, its skill imports the specialized repository-status method as a tool. Deployment binds that import to the repository-facing agent's MCP provider, which returns evidence grounded in its own domain context. The person profile contributes communication behavior without duplicating repository knowledge.

@@ -84,7 +84,7 @@ This arrangement preserves distinct responsibilities. The executive assistant ow
## 8. Diff as governance
-Traditional infrastructure tools made declarative diffs operationally important. TypeFerence applies the useful portion of that idea without coupling compilation to deployment. Its lifecycle is author, validate, resolve, compile, diff, and publish.
+Traditional infrastructure tools made declarative diffs operationally important. TypeFerence applies the useful portion of that idea while keeping identity separate from address. Its lifecycle is author, restore, validate, build, diff, link, and publish/run.
A change to an enterprise norm can be compiled across every concrete agent. Reviewers can inspect exactly which target artifacts changed. Provenance answers why a line exists and which embedded profile, agent, or skill supplied it. Capability validation prevents an apparently harmless specialization from silently changing what callers may send or expect.
@@ -92,9 +92,9 @@ This enables governance through normal software practices: pull requests, determ
## 9. Boundaries and future work
-The reference prototype does not manage deployment state, host models, or grant authority. It validates top-level JSON arguments but is not a complete JSON Schema engine. Target adapters demonstrate native shapes and should evolve alongside their platforms.
+The reference prototype consumes explicit deployment bindings but does not deploy services, manage host models, store secrets, or grant authority. The native context type language is deliberately smaller than JSON Schema. Target adapters demonstrate native shapes and should evolve alongside their platforms.
-Promising extensions include native ARD cards for deployed MCP and A2A targets, signed compiled bundles, richer schema validation, semantic diff summaries, policy linting, remote MCP transport, and conformance suites for third-party adapters.
+Promising extensions include linked ARD cards for additional deployed MCP targets, signed compiled bundles, semantic diff summaries, policy linting, version-range solving, and conformance suites for third-party adapters.
The important boundary should remain: portable mechanics in TypeFerence, behavioral authority in the organization, and execution authority in the host.
diff --git a/editors/vscode/.gitignore b/editors/vscode/.gitignore
new file mode 100644
index 0000000..d3e15b1
--- /dev/null
+++ b/editors/vscode/.gitignore
@@ -0,0 +1,3 @@
+node_modules/
+out/
+*.vsix
diff --git a/editors/vscode/README.md b/editors/vscode/README.md
new file mode 100644
index 0000000..e3d508d
--- /dev/null
+++ b/editors/vscode/README.md
@@ -0,0 +1,42 @@
+# TypeFerence for VS Code
+
+A thin VS Code client for the TypeFerence language server. It associates `.tfer`
+files with the `tfer` language (YAML frontmatter highlighted as YAML, the body as
+markdown) and launches `typeference-lsp` to provide:
+
+- shape and workspace-composition diagnostics,
+- completion of resource kinds and field names,
+- go-to-definition on resource ids,
+- document symbols.
+
+## Prerequisites
+
+Build and install the language server so it is on `PATH`:
+
+```sh
+make build-lsp # produces bin/typeference-lsp
+# put bin/ on PATH, or set the setting below
+```
+
+If it is not on `PATH`, set `"typeference.serverPath"` to its absolute location
+in your VS Code settings.
+
+## Develop / run
+
+```sh
+cd editors/vscode
+npm install
+```
+
+Then open this folder in VS Code and press F5 to launch an Extension
+Development Host, or package a `.vsix`:
+
+```sh
+npx @vscode/vsce package
+```
+
+## Status
+
+The extension source is complete and self-contained; the LSP server it drives
+(`internal/lsp`) is covered by Go tests. The extension itself has not been run
+end-to-end in CI — build it with `npm install` and load it to try it.
diff --git a/editors/vscode/extension.js b/editors/vscode/extension.js
new file mode 100644
index 0000000..0f16fcf
--- /dev/null
+++ b/editors/vscode/extension.js
@@ -0,0 +1,32 @@
+// Minimal VS Code client for the TypeFerence language server. It launches
+// `typeference-lsp` (which must be on PATH) and drives it over stdio for .tfer
+// sources: diagnostics, completion, go-to-definition, and document symbols.
+const { workspace, window } = require("vscode");
+const { LanguageClient, TransportKind } = require("vscode-languageclient/node");
+
+let client;
+
+function activate(context) {
+ const command = workspace.getConfiguration("typeference").get("serverPath") || "typeference-lsp";
+ const server = { command, transport: TransportKind.stdio };
+ const serverOptions = { run: server, debug: server };
+ const clientOptions = {
+ documentSelector: [{ scheme: "file", language: "tfer" }],
+ synchronize: {
+ fileEvents: workspace.createFileSystemWatcher("**/*.{tfer,yaml}"),
+ },
+ };
+ client = new LanguageClient("typeference", "TypeFerence", serverOptions, clientOptions);
+ client.start().catch((err) => {
+ window.showErrorMessage(
+ "TypeFerence: could not start typeference-lsp (" + command + "). Is it on PATH? " + err.message,
+ );
+ });
+ context.subscriptions.push({ dispose: () => client && client.stop() });
+}
+
+function deactivate() {
+ return client ? client.stop() : undefined;
+}
+
+module.exports = { activate, deactivate };
diff --git a/editors/vscode/language-configuration.json b/editors/vscode/language-configuration.json
new file mode 100644
index 0000000..f7f8418
--- /dev/null
+++ b/editors/vscode/language-configuration.json
@@ -0,0 +1,24 @@
+{
+ "comments": {
+ "lineComment": "#"
+ },
+ "brackets": [
+ ["{", "}"],
+ ["[", "]"],
+ ["(", ")"]
+ ],
+ "autoClosingPairs": [
+ ["{", "}"],
+ ["[", "]"],
+ ["(", ")"],
+ ["\"", "\""],
+ ["'", "'"]
+ ],
+ "surroundingPairs": [
+ ["{", "}"],
+ ["[", "]"],
+ ["(", ")"],
+ ["\"", "\""],
+ ["'", "'"]
+ ]
+}
diff --git a/editors/vscode/package-lock.json b/editors/vscode/package-lock.json
new file mode 100644
index 0000000..c76541f
--- /dev/null
+++ b/editors/vscode/package-lock.json
@@ -0,0 +1,107 @@
+{
+ "name": "typeference",
+ "version": "0.0.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "typeference",
+ "version": "0.0.1",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "vscode-languageclient": "^9.0.1"
+ },
+ "devDependencies": {
+ "@types/vscode": "^1.75.0"
+ },
+ "engines": {
+ "vscode": "^1.75.0"
+ }
+ },
+ "node_modules/@types/vscode": {
+ "version": "1.125.0",
+ "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.125.0.tgz",
+ "integrity": "sha512-0icm/ZQAaism87P0ekHqi4/Ju9du+Tm0RUW+y7vqRsxY2cY0FNRX1nAnaW7nT6npPt2tfHiheZ55Zm9UhqonFA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",
+ "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/vscode-jsonrpc": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
+ "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/vscode-languageclient": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz",
+ "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==",
+ "license": "MIT",
+ "dependencies": {
+ "minimatch": "^5.1.0",
+ "semver": "^7.3.7",
+ "vscode-languageserver-protocol": "3.17.5"
+ },
+ "engines": {
+ "vscode": "^1.82.0"
+ }
+ },
+ "node_modules/vscode-languageserver-protocol": {
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
+ "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
+ "license": "MIT",
+ "dependencies": {
+ "vscode-jsonrpc": "8.2.0",
+ "vscode-languageserver-types": "3.17.5"
+ }
+ },
+ "node_modules/vscode-languageserver-types": {
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
+ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
+ "license": "MIT"
+ }
+ }
+}
diff --git a/editors/vscode/package.json b/editors/vscode/package.json
new file mode 100644
index 0000000..4d3871d
--- /dev/null
+++ b/editors/vscode/package.json
@@ -0,0 +1,47 @@
+{
+ "name": "typeference",
+ "displayName": "TypeFerence",
+ "description": "Language support for TypeFerence .tfer sources — diagnostics, completion, go-to-definition, and symbols via typeference-lsp.",
+ "version": "0.0.1",
+ "publisher": "buchk",
+ "license": "Apache-2.0",
+ "engines": {
+ "vscode": "^1.75.0"
+ },
+ "categories": [
+ "Programming Languages",
+ "Linters"
+ ],
+ "activationEvents": [
+ "onLanguage:tfer"
+ ],
+ "main": "./extension.js",
+ "contributes": {
+ "languages": [
+ {
+ "id": "tfer",
+ "aliases": [
+ "TypeFerence",
+ "tfer"
+ ],
+ "extensions": [
+ ".tfer"
+ ],
+ "configuration": "./language-configuration.json"
+ }
+ ],
+ "grammars": [
+ {
+ "language": "tfer",
+ "scopeName": "source.tfer",
+ "path": "./syntaxes/tfer.tmLanguage.json"
+ }
+ ]
+ },
+ "dependencies": {
+ "vscode-languageclient": "^9.0.1"
+ },
+ "devDependencies": {
+ "@types/vscode": "^1.75.0"
+ }
+}
diff --git a/editors/vscode/syntaxes/tfer.tmLanguage.json b/editors/vscode/syntaxes/tfer.tmLanguage.json
new file mode 100644
index 0000000..d55fd7f
--- /dev/null
+++ b/editors/vscode/syntaxes/tfer.tmLanguage.json
@@ -0,0 +1,26 @@
+{
+ "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
+ "name": "TypeFerence",
+ "scopeName": "source.tfer",
+ "patterns": [
+ {
+ "comment": "YAML frontmatter between the leading --- fences",
+ "begin": "\\A(---)\\s*$",
+ "end": "^(---)\\s*$",
+ "beginCaptures": {
+ "1": { "name": "punctuation.definition.tag.begin.tfer" }
+ },
+ "endCaptures": {
+ "1": { "name": "punctuation.definition.tag.end.tfer" }
+ },
+ "name": "meta.embedded.block.frontmatter.tfer",
+ "patterns": [
+ { "include": "source.yaml" }
+ ]
+ },
+ {
+ "comment": "The body is markdown (skill instructions or context content)",
+ "include": "text.html.markdown"
+ }
+ ]
+}
diff --git a/examples/deployment/helio-a2a.yaml b/examples/deployment/helio-a2a.yaml
new file mode 100644
index 0000000..e55a381
--- /dev/null
+++ b/examples/deployment/helio-a2a.yaml
@@ -0,0 +1,32 @@
+schemaVersion: 1
+environment: local
+artifacts:
+ helio/executive-assistant@1.0.0:
+ modes: []
+ helio/payments-repo-agent@1.0.0:
+ modes: [a2a]
+providers:
+ payments-agent:
+ kind: mcp
+ transport: http
+ url: https://agents.helio.example/payments-repo-agent/mcp
+ repository-signals:
+ kind: mcp
+ transport: stdio
+ command: helio-repository-signals
+ args: [--bundle, "{bundle}"]
+ environment:
+ HELIO_REPOSITORY_TOKEN:
+ fromEnvironment: HELIO_REPOSITORY_TOKEN
+toolBindings:
+ helio/tools/payments-repository-agent@1.0.0:
+ provider: payments-agent
+ remoteName: repository_status
+ helio/tools/repository-signals@1.0.0:
+ provider: repository-signals
+ remoteName: repository_signals
+agentEndpoints:
+ helio/executive-assistant@1.0.0:
+ a2aUrl: https://agents.helio.example/executive-assistant
+ helio/payments-repo-agent@1.0.0:
+ a2aUrl: https://agents.helio.example/payments-repo-agent
diff --git a/examples/deployment/helio.yaml b/examples/deployment/helio.yaml
new file mode 100644
index 0000000..b084018
--- /dev/null
+++ b/examples/deployment/helio.yaml
@@ -0,0 +1,27 @@
+schemaVersion: 1
+environment: local
+artifacts:
+ helio/executive-assistant@1.0.0:
+ modes: []
+ helio/payments-repo-agent@1.0.0:
+ modes: [manual]
+providers:
+ payments-agent:
+ kind: mcp
+ transport: http
+ url: https://agents.helio.example/payments-repo-agent/mcp
+ repository-signals:
+ kind: mcp
+ transport: stdio
+ command: helio-repository-signals
+ args: [--bundle, "{bundle}"]
+ environment:
+ HELIO_REPOSITORY_TOKEN:
+ fromEnvironment: HELIO_REPOSITORY_TOKEN
+toolBindings:
+ helio/tools/payments-repository-agent@1.0.0:
+ provider: payments-agent
+ remoteName: repository_status
+ helio/tools/repository-signals@1.0.0:
+ provider: repository-signals
+ remoteName: repository_signals
diff --git a/examples/helio/agents/executive-assistant.agent.yaml b/examples/helio/agents/executive-assistant.agent.tfer
similarity index 78%
rename from examples/helio/agents/executive-assistant.agent.yaml
rename to examples/helio/agents/executive-assistant.agent.tfer
index 5bada56..dd2ce2b 100644
--- a/examples/helio/agents/executive-assistant.agent.yaml
+++ b/examples/helio/agents/executive-assistant.agent.tfer
@@ -1,11 +1,13 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: agent
id: helio/executive-assistant@1.0.0
displayName: Helio Executive Assistant
description: Coordinates an executive's correspondence, briefings, and cross-agent requests.
embeds:
- helio/profiles/person-defaults@1.0.0
-contextFiles:
- - context/executive-rhythm.md
+context:
+ - helio/context/executive-rhythm@1.0.0
skills:
- ref: helio/skills/prepare-brief@1.0.0
+---
diff --git a/examples/helio/agents/payments-repo-agent.agent.yaml b/examples/helio/agents/payments-repo-agent.agent.tfer
similarity index 82%
rename from examples/helio/agents/payments-repo-agent.agent.yaml
rename to examples/helio/agents/payments-repo-agent.agent.tfer
index 40fde20..ed01a62 100644
--- a/examples/helio/agents/payments-repo-agent.agent.yaml
+++ b/examples/helio/agents/payments-repo-agent.agent.tfer
@@ -1,12 +1,14 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: agent
id: helio/payments-repo-agent@1.0.0
displayName: Helio Payments Repository Agent
description: Specializes repository assistance for the fictional payments service.
embeds:
- helio/profiles/repository-defaults@1.0.0
-contextFiles:
- - context/payments-service.md
+context:
+ - helio/context/payments-service@1.0.0
skills:
- ref: helio/skills/payments-repository-status@1.0.0
capability: helio/capabilities/repository-status@1.0.0
+---
diff --git a/examples/helio/capabilities/prepare-brief.capability.yaml b/examples/helio/capabilities/prepare-brief.capability.tfer
similarity index 91%
rename from examples/helio/capabilities/prepare-brief.capability.yaml
rename to examples/helio/capabilities/prepare-brief.capability.tfer
index 3eca798..392a474 100644
--- a/examples/helio/capabilities/prepare-brief.capability.yaml
+++ b/examples/helio/capabilities/prepare-brief.capability.tfer
@@ -1,7 +1,10 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: capability
id: helio/capabilities/prepare-brief@1.0.0
displayName: Prepare Brief
description: Capability slot for assembling decision-oriented executive briefs.
+visibility: exposed
inputSchema: '{"type":"object","properties":{"topic":{"type":"string"},"evidence":{"type":"array","items":{"type":"string"}}},"required":["topic"],"additionalProperties":false}'
outputSchema: '{"type":"object","properties":{"brief":{"type":"string"},"openQuestions":{"type":"array","items":{"type":"string"}}},"required":["brief","openQuestions"]}'
+---
diff --git a/examples/helio/capabilities/repository-status.capability.yaml b/examples/helio/capabilities/repository-status.capability.tfer
similarity index 91%
rename from examples/helio/capabilities/repository-status.capability.yaml
rename to examples/helio/capabilities/repository-status.capability.tfer
index 27911ad..01c8adb 100644
--- a/examples/helio/capabilities/repository-status.capability.yaml
+++ b/examples/helio/capabilities/repository-status.capability.tfer
@@ -1,7 +1,10 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: capability
id: helio/capabilities/repository-status@1.0.0
displayName: Repository Status
description: Capability slot for evidence-backed repository health summaries.
+visibility: exposed
inputSchema: '{"type":"object","properties":{"focus":{"type":"string"}},"additionalProperties":false}'
outputSchema: '{"type":"object","properties":{"status":{"type":"string"},"evidence":{"type":"array","items":{"type":"string"}},"risks":{"type":"array","items":{"type":"string"}}},"required":["status","evidence","risks"]}'
+---
diff --git a/examples/helio/capabilities/triage-message.capability.yaml b/examples/helio/capabilities/triage-message.capability.tfer
similarity index 92%
rename from examples/helio/capabilities/triage-message.capability.yaml
rename to examples/helio/capabilities/triage-message.capability.tfer
index 9a2121e..602cd01 100644
--- a/examples/helio/capabilities/triage-message.capability.yaml
+++ b/examples/helio/capabilities/triage-message.capability.tfer
@@ -1,7 +1,10 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: capability
id: helio/capabilities/triage-message@1.0.0
displayName: Triage Message
description: Capability slot for classifying inbound messages and selecting accountable next actions.
+visibility: exposed
inputSchema: '{"type":"object","properties":{"message":{"type":"string"}},"required":["message"],"additionalProperties":false}'
outputSchema: '{"type":"object","properties":{"summary":{"type":"string"},"urgency":{"type":"string"},"owner":{"type":"string"},"nextAction":{"type":"string"}},"required":["summary","urgency","owner","nextAction"]}'
+---
diff --git a/examples/helio/context-types/executive-rhythm.contexttype.tfer b/examples/helio/context-types/executive-rhythm.contexttype.tfer
new file mode 100644
index 0000000..4cc0db4
--- /dev/null
+++ b/examples/helio/context-types/executive-rhythm.contexttype.tfer
@@ -0,0 +1,9 @@
+---
+schemaVersion: 4
+kind: contextType
+id: helio/context-types/executive-rhythm@1.0.0
+displayName: Executive Rhythm
+body:
+ type: text
+ required: true
+---
diff --git a/examples/helio/context-types/organization.contexttype.tfer b/examples/helio/context-types/organization.contexttype.tfer
new file mode 100644
index 0000000..ed1b9b1
--- /dev/null
+++ b/examples/helio/context-types/organization.contexttype.tfer
@@ -0,0 +1,9 @@
+---
+schemaVersion: 4
+kind: contextType
+id: helio/context-types/organization@1.0.0
+displayName: Organization
+body:
+ type: text
+ required: true
+---
diff --git a/examples/helio/context-types/payments-service.contexttype.tfer b/examples/helio/context-types/payments-service.contexttype.tfer
new file mode 100644
index 0000000..a0789fb
--- /dev/null
+++ b/examples/helio/context-types/payments-service.contexttype.tfer
@@ -0,0 +1,9 @@
+---
+schemaVersion: 4
+kind: contextType
+id: helio/context-types/payments-service@1.0.0
+displayName: Payments Service
+body:
+ type: text
+ required: true
+---
diff --git a/examples/helio/context-types/principal.contexttype.tfer b/examples/helio/context-types/principal.contexttype.tfer
new file mode 100644
index 0000000..4e7f5af
--- /dev/null
+++ b/examples/helio/context-types/principal.contexttype.tfer
@@ -0,0 +1,9 @@
+---
+schemaVersion: 4
+kind: contextType
+id: helio/context-types/principal@1.0.0
+displayName: Principal Preferences
+body:
+ type: text
+ required: true
+---
diff --git a/examples/helio/context-types/repository-evidence.contexttype.tfer b/examples/helio/context-types/repository-evidence.contexttype.tfer
new file mode 100644
index 0000000..ffb0f49
--- /dev/null
+++ b/examples/helio/context-types/repository-evidence.contexttype.tfer
@@ -0,0 +1,9 @@
+---
+schemaVersion: 4
+kind: contextType
+id: helio/context-types/repository-evidence@1.0.0
+displayName: Repository Evidence
+body:
+ type: text
+ required: true
+---
diff --git a/examples/helio/context-types/safety-policy.contexttype.tfer b/examples/helio/context-types/safety-policy.contexttype.tfer
new file mode 100644
index 0000000..10ce93c
--- /dev/null
+++ b/examples/helio/context-types/safety-policy.contexttype.tfer
@@ -0,0 +1,14 @@
+---
+schemaVersion: 4
+kind: contextType
+id: helio/context-types/safety-policy@1.0.0
+displayName: Safety Policy
+description: An organizational safety policy governing what an agent may do without explicit authority.
+fields:
+ authority:
+ type: string
+ required: true
+body:
+ type: text
+ required: true
+---
diff --git a/examples/helio/context/executive-rhythm.context.tfer b/examples/helio/context/executive-rhythm.context.tfer
new file mode 100644
index 0000000..83d89c4
--- /dev/null
+++ b/examples/helio/context/executive-rhythm.context.tfer
@@ -0,0 +1,10 @@
+---
+schemaVersion: 4
+kind: context
+id: helio/context/executive-rhythm@1.0.0
+contextType: helio/context-types/executive-rhythm@1.0.0
+displayName: Executive Rhythm
+---
+# Executive rhythm
+
+Daily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.
diff --git a/examples/helio/context/executive-rhythm.md b/examples/helio/context/executive-rhythm.md
deleted file mode 100644
index d3ec2cf..0000000
--- a/examples/helio/context/executive-rhythm.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Executive rhythm
-
-Daily briefs prioritize decisions due within 48 hours. Weekly briefs group information by outcome rather than reporting line.
diff --git a/examples/helio/context/organization.md b/examples/helio/context/organization.context.tfer
similarity index 53%
rename from examples/helio/context/organization.md
rename to examples/helio/context/organization.context.tfer
index 2c65c4e..f37e069 100644
--- a/examples/helio/context/organization.md
+++ b/examples/helio/context/organization.context.tfer
@@ -1,3 +1,10 @@
+---
+schemaVersion: 4
+kind: context
+id: helio/context/organization@1.0.0
+contextType: helio/context-types/organization@1.0.0
+displayName: Helio Works
+---
# Helio Works
Helio Works is a fictional organization used only to demonstrate TypeFerence. It values clear ownership, reversible decisions, and evidence-backed communication.
diff --git a/examples/helio/context/payments-service.md b/examples/helio/context/payments-service.context.tfer
similarity index 51%
rename from examples/helio/context/payments-service.md
rename to examples/helio/context/payments-service.context.tfer
index 86a8b18..3886755 100644
--- a/examples/helio/context/payments-service.md
+++ b/examples/helio/context/payments-service.context.tfer
@@ -1,3 +1,10 @@
+---
+schemaVersion: 4
+kind: context
+id: helio/context/payments-service@1.0.0
+contextType: helio/context-types/payments-service@1.0.0
+displayName: Payments Service
+---
# Payments service
This fictional service requires API-contract compatibility, reconciliation evidence, and a documented rollback path before a release is described as healthy.
diff --git a/examples/helio/context/principal.context.tfer b/examples/helio/context/principal.context.tfer
new file mode 100644
index 0000000..01d81d8
--- /dev/null
+++ b/examples/helio/context/principal.context.tfer
@@ -0,0 +1,10 @@
+---
+schemaVersion: 4
+kind: context
+id: helio/context/principal@1.0.0
+contextType: helio/context-types/principal@1.0.0
+displayName: Principal
+---
+# Principal
+
+The principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.
diff --git a/examples/helio/context/principal.md b/examples/helio/context/principal.md
deleted file mode 100644
index e4f694b..0000000
--- a/examples/helio/context/principal.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Principal
-
-The principal prefers short decision briefs that identify the owner, deadline, evidence, and unresolved risk.
diff --git a/examples/helio/context/repository.context.tfer b/examples/helio/context/repository.context.tfer
new file mode 100644
index 0000000..b885f71
--- /dev/null
+++ b/examples/helio/context/repository.context.tfer
@@ -0,0 +1,10 @@
+---
+schemaVersion: 4
+kind: context
+id: helio/context/repository-evidence@1.0.0
+contextType: helio/context-types/repository-evidence@1.0.0
+displayName: Repository Evidence
+---
+# Repository evidence
+
+Repository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.
diff --git a/examples/helio/context/repository.md b/examples/helio/context/repository.md
deleted file mode 100644
index e3716d0..0000000
--- a/examples/helio/context/repository.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Repository evidence
-
-Repository reports should identify the current branch, working-tree state, relevant checks, and source commit whenever available.
diff --git a/examples/helio/context/safety-policy.context.tfer b/examples/helio/context/safety-policy.context.tfer
new file mode 100644
index 0000000..ddc9f03
--- /dev/null
+++ b/examples/helio/context/safety-policy.context.tfer
@@ -0,0 +1,12 @@
+---
+schemaVersion: 4
+kind: context
+id: helio/context/safety-policy@1.0.0
+contextType: helio/context-types/safety-policy@1.0.0
+displayName: Helio Safety Policy
+values:
+ authority: explicit
+---
+# Safety policy
+
+Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
diff --git a/examples/helio/context/safety-policy.md b/examples/helio/context/safety-policy.md
deleted file mode 100644
index cd25063..0000000
--- a/examples/helio/context/safety-policy.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Safety policy
-
-Agents may prepare recommendations and drafts. They must not represent approval, transmit external messages, or make irreversible changes without explicit authority.
diff --git a/examples/helio/interfaces/correspondent.interface.yaml b/examples/helio/interfaces/correspondent.interface.tfer
similarity index 76%
rename from examples/helio/interfaces/correspondent.interface.yaml
rename to examples/helio/interfaces/correspondent.interface.tfer
index 7339374..ad4d196 100644
--- a/examples/helio/interfaces/correspondent.interface.yaml
+++ b/examples/helio/interfaces/correspondent.interface.tfer
@@ -1,9 +1,11 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: interface
id: helio/interfaces/correspondent@1.0.0
displayName: Correspondent
description: Contract for agents that assess and route communications.
requiresSlots:
- - principal
+ principal: helio/context-types/principal@1.0.0
requiresCapabilities:
- helio/capabilities/triage-message@1.0.0
+---
diff --git a/examples/helio/interfaces/repository-observer.interface.yaml b/examples/helio/interfaces/repository-observer.interface.tfer
similarity index 75%
rename from examples/helio/interfaces/repository-observer.interface.yaml
rename to examples/helio/interfaces/repository-observer.interface.tfer
index 375e5bf..9900e83 100644
--- a/examples/helio/interfaces/repository-observer.interface.yaml
+++ b/examples/helio/interfaces/repository-observer.interface.tfer
@@ -1,9 +1,11 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: interface
id: helio/interfaces/repository-observer@1.0.0
displayName: Repository Observer
description: Contract for agents that can report evidence-backed repository status.
requiresSlots:
- - repository
+ repository: helio/context-types/repository-evidence@1.0.0
requiresCapabilities:
- helio/capabilities/repository-status@1.0.0
+---
diff --git a/examples/helio/profiles/enterprise-defaults.profile.yaml b/examples/helio/profiles/enterprise-defaults.profile.tfer
similarity index 66%
rename from examples/helio/profiles/enterprise-defaults.profile.yaml
rename to examples/helio/profiles/enterprise-defaults.profile.tfer
index 8306dc9..b4184e6 100644
--- a/examples/helio/profiles/enterprise-defaults.profile.yaml
+++ b/examples/helio/profiles/enterprise-defaults.profile.tfer
@@ -1,14 +1,14 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: profile
id: helio/profiles/enterprise-defaults@1.0.0
displayName: Helio Enterprise Defaults
description: Shared organizational governance for every Helio resource.
slots:
- organization: context/organization.md
- safetyPolicy: context/safety-policy.md
+ organization: helio/context/organization@1.0.0
workingNorms:
- Preserve a clear audit trail for material decisions.
- State uncertainty and route work to an accountable owner when authority is unclear.
-contextFiles:
- - context/organization.md
- - context/safety-policy.md
+context:
+ - helio/context/safety-policy@1.0.0
+---
diff --git a/examples/helio/profiles/person-defaults.profile.yaml b/examples/helio/profiles/person-defaults.profile.tfer
similarity index 80%
rename from examples/helio/profiles/person-defaults.profile.yaml
rename to examples/helio/profiles/person-defaults.profile.tfer
index 73977ac..0cd8803 100644
--- a/examples/helio/profiles/person-defaults.profile.yaml
+++ b/examples/helio/profiles/person-defaults.profile.tfer
@@ -1,4 +1,5 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: profile
id: helio/profiles/person-defaults@1.0.0
displayName: Helio Person Defaults
@@ -6,10 +7,10 @@ description: Reusable behavior for respectful inbound and outbound assistance fo
embeds:
- helio/profiles/enterprise-defaults@1.0.0
slots:
- principal: context/principal.md
+ principal: helio/context/principal@1.0.0
workingNorms:
- Distinguish drafts from messages approved for delivery.
-contextFiles:
- - context/principal.md
skills:
- ref: helio/skills/triage-message@1.0.0
+ sealed: true
+---
diff --git a/examples/helio/profiles/repository-defaults.profile.yaml b/examples/helio/profiles/repository-defaults.profile.tfer
similarity index 80%
rename from examples/helio/profiles/repository-defaults.profile.yaml
rename to examples/helio/profiles/repository-defaults.profile.tfer
index 7fa7784..e3bb967 100644
--- a/examples/helio/profiles/repository-defaults.profile.yaml
+++ b/examples/helio/profiles/repository-defaults.profile.tfer
@@ -1,4 +1,5 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: profile
id: helio/profiles/repository-defaults@1.0.0
displayName: Helio Repository Defaults
@@ -6,10 +7,9 @@ description: Reusable delivery practices for software repository assistance.
embeds:
- helio/profiles/enterprise-defaults@1.0.0
slots:
- repository: context/repository.md
+ repository: helio/context/repository-evidence@1.0.0
workingNorms:
- Prefer evidence from tests, diffs, and version control over recollection.
-contextFiles:
- - context/repository.md
skills:
- ref: helio/skills/repository-status@1.0.0
+---
diff --git a/examples/helio/skills/payments-repository-status.skill.tfer b/examples/helio/skills/payments-repository-status.skill.tfer
new file mode 100644
index 0000000..dc88318
--- /dev/null
+++ b/examples/helio/skills/payments-repository-status.skill.tfer
@@ -0,0 +1,33 @@
+---
+schemaVersion: 4
+kind: skill
+id: helio/skills/payments-repository-status@1.0.0
+binds: helio/capabilities/repository-status@1.0.0
+displayName: Payments Repository Status
+description: Report payments-service health with contract and reconciliation evidence.
+requiresContextTypes:
+ - helio/context-types/payments-service@1.0.0
+requiresTools:
+ - helio/tools/repository-signals@1.0.0
+inputSchema: '{"type":"object","properties":{"focus":{"type":"string"}},"additionalProperties":false}'
+outputSchema: '{"type":"object","properties":{"status":{"type":"string"},"evidence":{"type":"array","items":{"type":"string"}},"risks":{"type":"array","items":{"type":"string"}}},"required":["status","evidence","risks"]}'
+variants:
+ pipeline:
+ instructions: |
+ Apply the repository-status capability for the payments service and emit only
+ the strict output object. Mark any unavailable financial-control signal as an
+ explicit null; do not report the service healthy when one is missing.
+ manual:
+ instructions: |
+ Apply the repository-status capability for the payments service, then include
+ payment-contract compatibility, reconciliation checks, and rollback readiness,
+ surfacing uncertainty conversationally. Do not report the service healthy when
+ any required financial-control signal is unavailable.
+ a2a:
+ instructions: |
+ Apply the repository-status capability for the payments service and return
+ attributed evidence for the calling agent — contract compatibility,
+ reconciliation, and rollback readiness — with no user-facing framing. Do not
+ report the service healthy when any required financial-control signal is
+ unavailable.
+---
diff --git a/examples/helio/skills/payments-repository-status.skill.yaml b/examples/helio/skills/payments-repository-status.skill.yaml
deleted file mode 100644
index a46bb33..0000000
--- a/examples/helio/skills/payments-repository-status.skill.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-schemaVersion: 3
-kind: skill
-id: helio/skills/payments-repository-status@1.0.0
-binds: helio/capabilities/repository-status@1.0.0
-displayName: Payments Repository Status
-description: Report payments-service health with contract and reconciliation evidence.
-contextFiles:
- - context/payments-service.md
-instructions: |
- Apply the repository-status capability, then include payment-contract compatibility, reconciliation checks, and rollback readiness.
- Do not report the service healthy when any required financial-control signal is unavailable.
-inputSchema: '{"type":"object","properties":{"focus":{"type":"string"}},"additionalProperties":false}'
-outputSchema: '{"type":"object","properties":{"status":{"type":"string"},"evidence":{"type":"array","items":{"type":"string"}},"risks":{"type":"array","items":{"type":"string"}}},"required":["status","evidence","risks"]}'
diff --git a/examples/helio/skills/prepare-brief.skill.yaml b/examples/helio/skills/prepare-brief.skill.tfer
similarity index 60%
rename from examples/helio/skills/prepare-brief.skill.yaml
rename to examples/helio/skills/prepare-brief.skill.tfer
index ba4c2d6..a4776fa 100644
--- a/examples/helio/skills/prepare-brief.skill.yaml
+++ b/examples/helio/skills/prepare-brief.skill.tfer
@@ -1,13 +1,17 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: skill
id: helio/skills/prepare-brief@1.0.0
binds: helio/capabilities/prepare-brief@1.0.0
displayName: Prepare Brief
description: Assemble an executive brief, requesting repository evidence when needed.
-contextFiles:
- - context/executive-rhythm.md
-instructions: |
- Build a decision-oriented brief from the supplied topic and evidence.
- When repository status is material, request `payments-repo-agent.repository-status` and incorporate its returned evidence with attribution.
+requiresContextTypes:
+ - helio/context-types/executive-rhythm@1.0.0
+requiresTools:
+ - helio/tools/payments-repository-agent@1.0.0
inputSchema: '{"type":"object","properties":{"topic":{"type":"string"},"evidence":{"type":"array","items":{"type":"string"}}},"required":["topic"],"additionalProperties":false}'
outputSchema: '{"type":"object","properties":{"brief":{"type":"string"},"openQuestions":{"type":"array","items":{"type":"string"}}},"required":["brief","openQuestions"]}'
+---
+Build a decision-oriented brief from the supplied topic and evidence.
+When repository status is material, use the payments repository agent tool and
+incorporate its returned evidence with attribution.
diff --git a/examples/helio/skills/repository-status.skill.yaml b/examples/helio/skills/repository-status.skill.tfer
similarity index 68%
rename from examples/helio/skills/repository-status.skill.yaml
rename to examples/helio/skills/repository-status.skill.tfer
index 0502cda..f80a76d 100644
--- a/examples/helio/skills/repository-status.skill.yaml
+++ b/examples/helio/skills/repository-status.skill.tfer
@@ -1,11 +1,12 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: skill
id: helio/skills/repository-status@1.0.0
binds: helio/capabilities/repository-status@1.0.0
displayName: Repository Status
description: Summarize repository health using current version-control and test evidence.
-instructions: |
- Inspect the requested repository signals. Report branch state, recent material changes, test evidence, risks, and the next accountable action.
- Mark any unavailable signal explicitly instead of inferring success.
inputSchema: '{"type":"object","properties":{"focus":{"type":"string"}},"additionalProperties":false}'
outputSchema: '{"type":"object","properties":{"status":{"type":"string"},"evidence":{"type":"array","items":{"type":"string"}},"risks":{"type":"array","items":{"type":"string"}}},"required":["status","evidence","risks"]}'
+---
+Inspect the requested repository signals. Report branch state, recent material changes, test evidence, risks, and the next accountable action.
+Mark any unavailable signal explicitly instead of inferring success.
diff --git a/examples/helio/skills/triage-message.skill.yaml b/examples/helio/skills/triage-message.skill.tfer
similarity index 66%
rename from examples/helio/skills/triage-message.skill.yaml
rename to examples/helio/skills/triage-message.skill.tfer
index 8d0e69f..8d480d6 100644
--- a/examples/helio/skills/triage-message.skill.yaml
+++ b/examples/helio/skills/triage-message.skill.tfer
@@ -1,11 +1,14 @@
-schemaVersion: 3
+---
+schemaVersion: 4
kind: skill
id: helio/skills/triage-message@1.0.0
binds: helio/capabilities/triage-message@1.0.0
displayName: Triage Message
description: Classify an inbound message and recommend an accountable next action.
-instructions: |
- Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.
- Separate facts from assumptions. Return a concise recommendation; do not send a reply.
+requiresContextTypes:
+ - helio/context-types/safety-policy@1.0.0
inputSchema: '{"type":"object","properties":{"message":{"type":"string"}},"required":["message"],"additionalProperties":false}'
outputSchema: '{"type":"object","properties":{"summary":{"type":"string"},"urgency":{"type":"string"},"owner":{"type":"string"},"nextAction":{"type":"string"}},"required":["summary","urgency","owner","nextAction"]}'
+---
+Read the message and identify its sender, intent, urgency, decision owner, and requested deadline.
+Separate facts from assumptions. Return a concise recommendation; do not send a reply.
diff --git a/examples/helio/tools/payments-repository-agent.tool.tfer b/examples/helio/tools/payments-repository-agent.tool.tfer
new file mode 100644
index 0000000..318f394
--- /dev/null
+++ b/examples/helio/tools/payments-repository-agent.tool.tfer
@@ -0,0 +1,9 @@
+---
+schemaVersion: 4
+kind: tool
+id: helio/tools/payments-repository-agent@1.0.0
+displayName: Payments Repository Agent
+description: Runtime import for evidence-backed payments repository status.
+inputSchema: '{"type":"object","properties":{"focus":{"type":"string"}},"additionalProperties":false}'
+outputSchema: '{"type":"object","properties":{"status":{"type":"string"},"evidence":{"type":"array","items":{"type":"string"}},"risks":{"type":"array","items":{"type":"string"}}},"required":["status","evidence","risks"]}'
+---
diff --git a/examples/helio/tools/repository-signals.tool.tfer b/examples/helio/tools/repository-signals.tool.tfer
new file mode 100644
index 0000000..e10c1aa
--- /dev/null
+++ b/examples/helio/tools/repository-signals.tool.tfer
@@ -0,0 +1,11 @@
+---
+schemaVersion: 4
+kind: tool
+id: helio/tools/repository-signals@1.0.0
+displayName: Repository Signals
+description: >-
+ Reads version-control and test signals for a repository at runtime. TypeFerence
+ declares this extern; the deployer implements the body (ADR-0017, Door B).
+inputSchema: '{"type":"object","properties":{"repository":{"type":"string"}},"required":["repository"],"additionalProperties":false}'
+outputSchema: '{"type":"object","properties":{"branch":{"type":"string"},"tests":{"type":"string"}}}'
+---
diff --git a/examples/helio/typeference.yaml b/examples/helio/typeference.yaml
new file mode 100644
index 0000000..95598b0
--- /dev/null
+++ b/examples/helio/typeference.yaml
@@ -0,0 +1,4 @@
+schemaVersion: 2
+name: helio/agents
+version: 1.0.0
+publisher: helio.example
diff --git a/examples/repo-agent/README.md b/examples/repo-agent/README.md
new file mode 100644
index 0000000..a292468
--- /dev/null
+++ b/examples/repo-agent/README.md
@@ -0,0 +1,43 @@
+# repo-agent example
+
+A small corpus that exercises the object-model features from ADRs 0012–0019 in
+one place. Build it with the Go implementation:
+
+```sh
+typeference validate examples/repo-agent
+typeference build examples/repo-agent --target neutral --out out
+typeference inspect acme/repo-agent@1.0.0 --source examples/repo-agent
+```
+
+What each piece demonstrates:
+
+- **Exposure / visibility (ADR-0015).** `capabilities/repository-status` is
+ `visibility: exposed`, so the resolved skill is part of the agent's public
+ callable surface. A native callable card is materialized only after a
+ deployment endpoint/provider is linked.
+- **Mode variants (ADR-0012).** `skills/repository-status` declares `variants`
+ for `pipeline`, `manual`, and `a2a` instead of a single `instructions`. The
+ neutral bundle emits a `variants` member with each mode's additive
+ requirements; build does not flatten them.
+- **Tools as extern (ADR-0017).** `tools/vault-reader` is a `tool` declaration
+ (an extern header). The skill's `requiresTools` names it, checked at build and
+ required to have a provider binding at link. The tool's *body* is implemented
+ by the deployer, not TypeFerence.
+- **Typed context + refinement (ADR-0013).** `context-types/governed-cast`
+ embeds `cast-of-characters` (refinement). The skill `requiresContextTypes:
+ [cast-of-characters]`; the agent holds `notes/team` — a `governed-cast` — and
+ a governed cast *is a* cast, so the requirement is satisfied structurally.
+- **Schema-directed fields (ADR-0020).** `governed-cast` declares a native
+ `fields` mapping requiring `owner`; `notes/team` carries it under `values`.
+ Remove it and the build fails with a missing-required-field
+ error — required fields accumulate across the refinement closure.
+- **Sealing (ADR-0016).** `profiles/repo-defaults` binds the status skill
+ `sealed: true`. An agent embedding the profile may extend around it but cannot
+ override or rebind the sealed capability.
+- **`.tfer` format (ADR-0013).** `notes/team.context.tfer` is authored as
+ frontmatter (typed fields) plus a markdown body (the roster content).
+
+Change the corpus to see the type system push back: drop `context:` from the
+agent and the `requiresContextTypes` check fails; add a second binding of
+`repository-status` on the agent and sealing rejects the override; delete the
+`vault-reader` tool and `requiresTools` fails.
diff --git a/examples/repo-agent/agents/repo-agent.agent.tfer b/examples/repo-agent/agents/repo-agent.agent.tfer
new file mode 100644
index 0000000..650e657
--- /dev/null
+++ b/examples/repo-agent/agents/repo-agent.agent.tfer
@@ -0,0 +1,11 @@
+---
+schemaVersion: 4
+kind: agent
+id: acme/repo-agent@1.0.0
+displayName: Acme Repo Agent
+description: Reports repository status across pipeline, manual, and a2a surfaces.
+embeds:
+ - acme/profiles/repo-defaults@1.0.0
+context:
+ - acme/notes/team@1.0.0
+---
diff --git a/examples/repo-agent/capabilities/repository-status.capability.tfer b/examples/repo-agent/capabilities/repository-status.capability.tfer
new file mode 100644
index 0000000..2383283
--- /dev/null
+++ b/examples/repo-agent/capabilities/repository-status.capability.tfer
@@ -0,0 +1,8 @@
+---
+schemaVersion: 4
+kind: capability
+id: acme/capabilities/repository-status@1.0.0
+displayName: Repository Status
+description: Evidence-backed repository health summary.
+visibility: exposed
+---
diff --git a/examples/repo-agent/context-types/cast-of-characters.contexttype.tfer b/examples/repo-agent/context-types/cast-of-characters.contexttype.tfer
new file mode 100644
index 0000000..9187222
--- /dev/null
+++ b/examples/repo-agent/context-types/cast-of-characters.contexttype.tfer
@@ -0,0 +1,10 @@
+---
+schemaVersion: 4
+kind: contextType
+id: acme/context-types/cast-of-characters@1.0.0
+displayName: Cast of Characters
+description: The people an agent must know — names, roles, relationships.
+body:
+ type: text
+ required: true
+---
diff --git a/examples/repo-agent/context-types/governed-cast.contexttype.tfer b/examples/repo-agent/context-types/governed-cast.contexttype.tfer
new file mode 100644
index 0000000..f5a726a
--- /dev/null
+++ b/examples/repo-agent/context-types/governed-cast.contexttype.tfer
@@ -0,0 +1,13 @@
+---
+schemaVersion: 4
+kind: contextType
+id: acme/context-types/governed-cast@1.0.0
+displayName: Governed Cast of Characters
+description: A cast of characters refined with provenance and ownership metadata.
+fields:
+ owner:
+ type: string
+ required: true
+embeds:
+ - acme/context-types/cast-of-characters@1.0.0
+---
diff --git a/examples/repo-agent/notes/team.context.tfer b/examples/repo-agent/notes/team.context.tfer
new file mode 100644
index 0000000..f99c9a8
--- /dev/null
+++ b/examples/repo-agent/notes/team.context.tfer
@@ -0,0 +1,14 @@
+---
+schemaVersion: 4
+kind: context
+id: acme/notes/team@1.0.0
+contextType: acme/context-types/governed-cast@1.0.0
+displayName: Team Roster
+values:
+ owner: Dana
+---
+# Team
+
+- Dana — repository owner; approves releases.
+- Sam — on-call engineer; triages CI failures.
+- Priya — security reviewer; owns the release policy.
diff --git a/examples/repo-agent/profiles/repo-defaults.profile.tfer b/examples/repo-agent/profiles/repo-defaults.profile.tfer
new file mode 100644
index 0000000..28dcc86
--- /dev/null
+++ b/examples/repo-agent/profiles/repo-defaults.profile.tfer
@@ -0,0 +1,10 @@
+---
+schemaVersion: 4
+kind: profile
+id: acme/profiles/repo-defaults@1.0.0
+displayName: Repository Defaults
+description: Shared repository behavior; the status skill is sealed against override.
+skills:
+ - ref: acme/skills/repository-status@1.0.0
+ sealed: true
+---
diff --git a/examples/repo-agent/skills/repository-status.skill.tfer b/examples/repo-agent/skills/repository-status.skill.tfer
new file mode 100644
index 0000000..bffa044
--- /dev/null
+++ b/examples/repo-agent/skills/repository-status.skill.tfer
@@ -0,0 +1,24 @@
+---
+schemaVersion: 4
+kind: skill
+id: acme/skills/repository-status@1.0.0
+binds: acme/capabilities/repository-status@1.0.0
+displayName: Repository Status
+description: Summarize repository health, delivered per invocation surface.
+requiresTools:
+ - acme/tools/vault-reader@1.0.0
+requiresContextTypes:
+ - acme/context-types/cast-of-characters@1.0.0
+variants:
+ pipeline:
+ instructions: |
+ Emit strict JSON only. No prose. Never ask a clarifying question.
+ Mark every unavailable signal as an explicit null.
+ manual:
+ instructions: |
+ Explain the reasoning, surface risks conversationally, and offer the next
+ accountable action. Ask when a signal is ambiguous.
+ a2a:
+ instructions: |
+ Return attributed evidence for a calling agent. No user-facing framing.
+---
diff --git a/examples/repo-agent/tools/vault-reader.tool.tfer b/examples/repo-agent/tools/vault-reader.tool.tfer
new file mode 100644
index 0000000..2f3d8b5
--- /dev/null
+++ b/examples/repo-agent/tools/vault-reader.tool.tfer
@@ -0,0 +1,10 @@
+---
+schemaVersion: 4
+kind: tool
+id: acme/tools/vault-reader@1.0.0
+displayName: Vault Reader
+description: >-
+ Reads notes from a knowledge vault at runtime. TypeFerence declares this
+ extern (its interface and, later, its auth/scope shape); the body is
+ implemented by the deployer (ADR-0017, ADR-0019 Door B).
+---
diff --git a/go/cmd/playground-pack/main.go b/go/cmd/playground-pack/main.go
index 14b0205..2e6bf97 100644
--- a/go/cmd/playground-pack/main.go
+++ b/go/cmd/playground-pack/main.go
@@ -185,32 +185,32 @@ rubric:
}
var starterFiles = map[string]string{
- "agents/support-agent.agent.yaml": `schemaVersion: 3
+ "agents/support-agent.agent.yaml": `schemaVersion: 4
kind: agent
id: acme/support-agent@1.0.0
displayName: Acme Support Agent
description: Answers customer tickets for Acme's widget line.
embeds:
- acme/profiles/support-defaults@1.0.0
-contextFiles:
- - context/widgets.md
+context:
+ - acme/context/widgets@1.0.0
skills:
- ref: acme/skills/summarize-ticket@1.0.0
capability: acme/capabilities/summarize-ticket@1.0.0
`,
- "profiles/support-defaults.profile.yaml": `schemaVersion: 3
+ "profiles/support-defaults.profile.yaml": `schemaVersion: 4
kind: profile
id: acme/profiles/support-defaults@1.0.0
displayName: Acme Support Defaults
description: Reusable tone and escalation defaults for support agents.
slots:
- tone: context/tone.md
+ tone: acme/context/tone@1.0.0
workingNorms:
- Never promise a refund without a linked policy clause.
-contextFiles:
- - context/tone.md
+context:
+ - acme/context/tone@1.0.0
`,
- "capabilities/summarize-ticket.capability.yaml": `schemaVersion: 3
+ "capabilities/summarize-ticket.capability.yaml": `schemaVersion: 4
kind: capability
id: acme/capabilities/summarize-ticket@1.0.0
displayName: Summarize Ticket
@@ -218,19 +218,21 @@ description: Capability slot for structured ticket summaries.
inputSchema: '{"type":"object","properties":{"ticketId":{"type":"string"}},"additionalProperties":false}'
outputSchema: '{"type":"object","properties":{"summary":{"type":"string"},"nextAction":{"type":"string"}},"required":["summary","nextAction"]}'
`,
- "skills/summarize-ticket.skill.yaml": `schemaVersion: 3
+ "skills/summarize-ticket.skill.yaml": `schemaVersion: 4
kind: skill
id: acme/skills/summarize-ticket@1.0.0
binds: acme/capabilities/summarize-ticket@1.0.0
displayName: Summarize Ticket
description: Summarize a support ticket with the customer's history in view.
+requiresContextTypes:
+ - acme/context-types/widgets@1.0.0
instructions: |
Read the ticket and produce a two-sentence summary plus one concrete next action.
Cite the ticket fields you used; never invent order numbers.
inputSchema: '{"type":"object","properties":{"ticketId":{"type":"string"}},"additionalProperties":false}'
outputSchema: '{"type":"object","properties":{"summary":{"type":"string"},"nextAction":{"type":"string"}},"required":["summary","nextAction"]}'
`,
- "interfaces/summarizer.interface.yaml": `schemaVersion: 3
+ "interfaces/summarizer.interface.yaml": `schemaVersion: 4
kind: interface
id: acme/interfaces/summarizer@1.0.0
displayName: Summarizer
@@ -238,12 +240,40 @@ description: Contract for agents that can produce structured ticket summaries.
requiresCapabilities:
- acme/capabilities/summarize-ticket@1.0.0
`,
- "context/tone.md": `# Tone
+ "context-types/tone.contexttype.yaml": `schemaVersion: 4
+kind: contextType
+id: acme/context-types/tone@1.0.0
+body:
+ type: text
+ required: true
+`,
+ "context/tone.context.tfer": `---
+schemaVersion: 4
+kind: context
+id: acme/context/tone@1.0.0
+contextType: acme/context-types/tone@1.0.0
+displayName: Tone
+---
+# Tone
Warm, direct, and concrete. Lead with what will happen next, not with an
apology. One idea per sentence.
`,
- "context/widgets.md": `# Widget line
+ "context-types/widgets.contexttype.yaml": `schemaVersion: 4
+kind: contextType
+id: acme/context-types/widgets@1.0.0
+body:
+ type: text
+ required: true
+`,
+ "context/widgets.context.tfer": `---
+schemaVersion: 4
+kind: context
+id: acme/context/widgets@1.0.0
+contextType: acme/context-types/widgets@1.0.0
+displayName: Widget Line
+---
+# Widget line
Acme sells three widget models: Standard, Pro, and the discontinued Classic.
Classic tickets always require the legacy-parts disclaimer.
diff --git a/go/cmd/typeference-lsp/main.go b/go/cmd/typeference-lsp/main.go
new file mode 100644
index 0000000..dcf43e2
--- /dev/null
+++ b/go/cmd/typeference-lsp/main.go
@@ -0,0 +1,21 @@
+// Command typeference-lsp is a Language Server Protocol server for TypeFerence
+// sources (.tfer and .yaml). It speaks JSON-RPC 2.0 over stdio and publishes
+// per-document authoring diagnostics (see internal/lsp for the v0 scope).
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/buchk/TypeFerence/go/internal/lsp"
+)
+
+// version is stamped by the release workflow via -ldflags "-X main.version=...".
+var version = "dev"
+
+func main() {
+ if err := lsp.NewServer(version).Run(os.Stdin, os.Stdout); err != nil {
+ fmt.Fprintf(os.Stderr, "typeference-lsp: %s\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/go/cmd/typeference/main.go b/go/cmd/typeference/main.go
index 35537e3..90013e5 100644
--- a/go/cmd/typeference/main.go
+++ b/go/cmd/typeference/main.go
@@ -5,15 +5,20 @@
package main
import (
+ "bytes"
+ "encoding/json"
"fmt"
+ "net/http"
"os"
"path/filepath"
"slices"
"strings"
"github.com/buchk/TypeFerence/go/internal/compile"
+ "github.com/buchk/TypeFerence/go/internal/deploy"
"github.com/buchk/TypeFerence/go/internal/eval"
"github.com/buchk/TypeFerence/go/internal/jsonx"
+ "github.com/buchk/TypeFerence/go/internal/packages"
"github.com/buchk/TypeFerence/go/internal/resource"
)
@@ -36,10 +41,20 @@ func run(args []string) int {
code, err = validate(args)
case "build":
code, err = build(args)
+ case "pack":
+ code, err = pack(args)
+ case "restore":
+ code, err = restore(args, false)
+ case "update":
+ code, err = restore(args, true)
+ case "link":
+ code, err = link(args)
case "inspect":
code, err = inspect(args)
case "diff":
code, err = diff(args)
+ case "publish":
+ code, err = publish(args)
case "eval":
code, err = evalCommand(args)
case "equivalence":
@@ -65,7 +80,11 @@ func validate(args []string) (int, error) {
if err != nil {
return 0, err
}
- agents, err := compile.Validate(source, trustConfig)
+ packagesDir, err := option(args, "--packages-dir")
+ if err != nil {
+ return 0, err
+ }
+ agents, err := compile.ValidateWithPackages(source, trustConfig, packagesDir)
if err != nil {
return 0, err
}
@@ -90,7 +109,7 @@ func build(args []string) (int, error) {
} else if v != "" {
target = v
}
- ard, err := ardOptions(args)
+ ard, err := ardOptions(args, source)
if err != nil {
return 0, err
}
@@ -98,7 +117,11 @@ func build(args []string) (int, error) {
if err != nil {
return 0, err
}
- files, err := compile.Build(source, output, targets, ard)
+ packagesDir, err := option(args, "--packages-dir")
+ if err != nil {
+ return 0, err
+ }
+ files, err := compile.BuildWithOptions(source, output, targets, ard, compile.BuildOptions{PackagesDir: packagesDir})
if err != nil {
return 0, err
}
@@ -115,6 +138,89 @@ func build(args []string) (int, error) {
return 0, nil
}
+func pack(args []string) (int, error) {
+ source, err := requiredArg(args, 1, "source")
+ if err != nil {
+ return 0, err
+ }
+ output, err := option(args, "--out")
+ if err != nil {
+ return 0, err
+ }
+ digest, err := packages.Pack(source, output)
+ if err != nil {
+ return 0, err
+ }
+ fmt.Printf("Packed %s\n", digest)
+ return 0, nil
+}
+
+func restore(args []string, update bool) (int, error) {
+ source, err := requiredArg(args, 1, "source")
+ if err != nil {
+ return 0, err
+ }
+ feeds, err := option(args, "--feeds")
+ if err != nil {
+ return 0, err
+ }
+ config, err := packages.LoadFeedConfig(feeds)
+ if err != nil {
+ return 0, err
+ }
+ packagesDir, err := option(args, "--packages-dir")
+ if err != nil {
+ return 0, err
+ }
+ locked := slices.Contains(args, "--locked")
+ if update && locked {
+ return 0, resource.Errorf("update cannot be combined with --locked")
+ }
+ if !update && !locked {
+ existing, err := packages.LoadLock(source)
+ if err != nil {
+ return 0, err
+ }
+ locked = existing != nil
+ }
+ restorer := packages.Restorer{
+ Source: source, PackagesDir: packagesDir, Config: config, Locked: locked,
+ }
+ lock, err := restorer.Restore()
+ if err != nil {
+ return 0, err
+ }
+ fmt.Printf("Restored %d packages%s.\n", len(lock.Packages), map[bool]string{true: " and updated the lockfile", false: ""}[update])
+ return 0, nil
+}
+
+func link(args []string) (int, error) {
+ input, err := requiredArg(args, 1, "built target")
+ if err != nil {
+ return 0, err
+ }
+ deployment, err := option(args, "--deployment")
+ if err != nil {
+ return 0, err
+ }
+ if deployment == "" {
+ return 0, resource.Errorf("--deployment is required")
+ }
+ output, err := option(args, "--out")
+ if err != nil {
+ return 0, err
+ }
+ if output == "" {
+ return 0, resource.Errorf("--out is required")
+ }
+ files, err := deploy.Link(input, deployment, output)
+ if err != nil {
+ return 0, err
+ }
+ fmt.Printf("Linked %d files at %s\n", len(files), output)
+ return 0, nil
+}
+
func inspect(args []string) (int, error) {
source := "."
if v, err := option(args, "--source"); err != nil {
@@ -126,7 +232,11 @@ func inspect(args []string) (int, error) {
if err != nil {
return 0, err
}
- agents, err := compile.Validate(source, "")
+ packagesDir, err := option(args, "--packages-dir")
+ if err != nil {
+ return 0, err
+ }
+ agents, err := compile.ValidateWithPackages(source, "", packagesDir)
if err != nil {
return 0, err
}
@@ -157,7 +267,7 @@ func diff(args []string) (int, error) {
}
defer os.RemoveAll(temp)
- ard, err := ardOptions(args)
+ ard, err := ardOptions(args, source)
if err != nil {
return 0, err
}
@@ -171,7 +281,11 @@ func diff(args []string) (int, error) {
if err != nil {
return 0, err
}
- if _, err := compile.Build(source, temp, targets, ard); err != nil {
+ packagesDir, err := option(args, "--packages-dir")
+ if err != nil {
+ return 0, err
+ }
+ if _, err := compile.BuildWithOptions(source, temp, targets, ard, compile.BuildOptions{PackagesDir: packagesDir}); err != nil {
return 0, err
}
result, err := compile.CompareDirs(against, temp)
@@ -205,6 +319,68 @@ func diff(args []string) (int, error) {
return 0, nil
}
+// publish registers a compiled ARD catalog with a registry. It is the
+// deployment edge, not the deterministic core (ADR-0018): registry lifecycle is
+// disclaimed by core semantics, so this is an optional, side-effecting verb.
+// Without --registry it is a dry run that only summarizes the catalog.
+func publish(args []string) (int, error) {
+ dir, err := requiredArg(args, 1, "ard directory")
+ if err != nil {
+ return 0, err
+ }
+ catalogPath := filepath.Join(dir, "ai-catalog.json")
+ data, readErr := os.ReadFile(catalogPath)
+ if readErr != nil {
+ return 0, resource.Errorf("No ai-catalog.json under %s; build with --emit-ard first", dir)
+ }
+ var catalog struct {
+ Entries []struct {
+ Identifier string `json:"identifier"`
+ Type string `json:"type"`
+ } `json:"entries"`
+ }
+ if json.Unmarshal(data, &catalog) != nil {
+ return 0, resource.Errorf("Invalid ai-catalog.json: %s", catalogPath)
+ }
+ registry, err := option(args, "--registry")
+ if err != nil {
+ return 0, err
+ }
+ if registry == "" {
+ fmt.Printf("Dry run: %d catalog entries in %s (pass --registry to publish)\n", len(catalog.Entries), catalogPath)
+ for _, e := range catalog.Entries {
+ fmt.Printf(" %s %s\n", entryKind(e.Type), e.Identifier)
+ }
+ return 0, nil
+ }
+ resp, postErr := http.Post(registry, "application/json", bytes.NewReader(data))
+ if postErr != nil {
+ return 0, resource.Errorf("Publish failed: %s", postErr)
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode >= 300 {
+ return 1, resource.Errorf("Registry returned %s", resp.Status)
+ }
+ fmt.Printf("Published %d entries to %s (%s)\n", len(catalog.Entries), registry, resp.Status)
+ return 0, nil
+}
+
+// entryKind maps an ARD entry media type to a short label.
+func entryKind(mediaType string) string {
+ switch {
+ case strings.Contains(mediaType, "a2a-agent-card"):
+ return "a2a "
+ case strings.Contains(mediaType, "mcp-server"):
+ return "mcp "
+ case strings.Contains(mediaType, "source-package"):
+ return "source "
+ case strings.Contains(mediaType, "target-bundle"):
+ return "bundle "
+ default:
+ return "entry "
+ }
+}
+
func evalCommand(args []string) (int, error) {
source, err := requiredArg(args, 1, "source")
if err != nil {
@@ -306,7 +482,7 @@ func stringArr(values []string) jsonx.Arr {
return arr
}
-func ardOptions(args []string) (*compile.ArdPublicationOptions, error) {
+func ardOptions(args []string, source string) (*compile.ArdPublicationOptions, error) {
publisherDomain, err := option(args, "--publisher-domain")
if err != nil {
return nil, err
@@ -321,8 +497,21 @@ func ardOptions(args []string) (*compile.ArdPublicationOptions, error) {
}
allowUnsignedTrust := slices.Contains(args, "--allow-unsigned-trust")
emitArd := slices.Contains(args, "--emit-ard")
+ // A project manifest (typeference.yaml) with a publisher makes ARD emission
+ // the default and supplies the publisher domain, so `compile -> pushable
+ // ai-catalog.json` needs no flags (ADR-0018).
+ project, projErr := resource.LoadProject(source)
+ if projErr != nil {
+ return nil, projErr
+ }
+ if project != nil && strings.TrimSpace(project.Publisher) != "" {
+ emitArd = true
+ if publisherDomain == "" {
+ publisherDomain = project.Publisher
+ }
+ }
if emitArd && publisherDomain == "" {
- return nil, resource.Errorf("--emit-ard requires --publisher-domain")
+ return nil, resource.Errorf("--emit-ard requires --publisher-domain (or a `publisher` in typeference.yaml)")
}
if !emitArd && publisherDomain != "" {
return nil, resource.Errorf("--publisher-domain requires --emit-ard")
@@ -376,15 +565,26 @@ func help() int {
Commands:
typeference validate [--trust-config path]
+ [--packages-dir obj/typeference/packages]
+ typeference pack [--out package.tferpkg]
+ typeference restore --feeds [--locked]
+ [--packages-dir obj/typeference/packages]
+typeference update --feeds
+ [--packages-dir obj/typeference/packages]
typeference build [--target all|neutral|codex|copilot|cursor] [--out dist]
+ [--packages-dir obj/typeference/packages]
[--emit-ard --publisher-domain example.com] [--trust-config path]
[--trust-signatures signatures.json]
[--allow-unsigned-trust]
typeference inspect [--source path]
+ typeference link --deployment --out
typeference diff --against [--target all]
[--emit-ard --publisher-domain example.com] [--trust-config path]
[--trust-signatures signatures.json] [--json]
[--allow-unsigned-trust]
+ typeference publish [--registry ]
+ (deployment edge, not core: summarizes the compiled ARD catalog; with
+ --registry POSTs it to a registry. A dry run without --registry.)
typeference eval --scenarios [--live] [--model id] [--out dir]
(dry run by default: validates scenarios and emits exact request
payloads without calling any API; --live reads ANTHROPIC_API_KEY)
diff --git a/go/conformance/conformance_test.go b/go/conformance/conformance_test.go
index b76e90a..6df6421 100644
--- a/go/conformance/conformance_test.go
+++ b/go/conformance/conformance_test.go
@@ -12,6 +12,8 @@ import (
"os"
"path/filepath"
"sort"
+ "strconv"
+ "strings"
"testing"
"github.com/buchk/TypeFerence/go/internal/compile"
@@ -95,7 +97,17 @@ func runFixture(t *testing.T, dir string) {
ard.TrustSignaturesPath = filepath.Join(dir, m.TrustSignatures)
}
}
- _, buildErr := compile.Build(source, out, []compile.Target{compile.Neutral, compile.Codex, compile.Copilot, compile.Cursor}, ard)
+ // Fixtures 001-026 preserve the retired v3 byte contract. This explicit
+ // test-only switch does not reopen v3 on CLI/default compiler paths or for
+ // current-version fixtures.
+ legacyV3 := false
+ if prefix, _, ok := strings.Cut(filepath.Base(dir), "-"); ok {
+ number, _ := strconv.Atoi(prefix)
+ legacyV3 = number > 0 && number <= 26
+ }
+ _, buildErr := compile.BuildWithOptions(source, out,
+ []compile.Target{compile.Neutral, compile.Codex, compile.Copilot, compile.Cursor},
+ ard, compile.BuildOptions{LegacyV3: legacyV3})
if m.Expect == "error" {
if buildErr == nil {
diff --git a/go/internal/compile/ard.go b/go/internal/compile/ard.go
index 78a9259..045d928 100644
--- a/go/internal/compile/ard.go
+++ b/go/internal/compile/ard.go
@@ -7,6 +7,7 @@ import (
"strings"
"github.com/buchk/TypeFerence/go/internal/jsonx"
+ "github.com/buchk/TypeFerence/go/internal/packages"
"github.com/buchk/TypeFerence/go/internal/resolve"
"github.com/buchk/TypeFerence/go/internal/resource"
"github.com/buchk/TypeFerence/go/internal/trust"
@@ -36,14 +37,27 @@ func writeArdCatalog(
return resource.Errorf("Source directory not found: %s", source)
}
sourceName := urnSegment(filepath.Base(strings.TrimRight(sourceAbs, `\/`)))
+ sourceVersion := "1.0.0"
+ project, projErr := resource.LoadProject(source)
+ if projErr != nil {
+ return projErr
+ }
+ if project != nil {
+ if strings.TrimSpace(project.Name) != "" {
+ sourceName = urnSegment(project.Name)
+ }
+ if strings.TrimSpace(project.Version) != "" {
+ sourceVersion = project.Version
+ }
+ }
sourceIdentifier := "urn:air:" + publisherDomain + ":typeference:source:" + sourceName
- sourceHash, err := HashDirectory(source)
+ sourceHash, err := HashSource(source)
if err != nil {
return err
}
sourceDigest := "sha256:" + sourceHash
- sourceFiles, err := packageFiles(source)
+ sourceFiles, err := sourcePackageFiles(source)
if err != nil {
return err
}
@@ -55,7 +69,7 @@ func writeArdCatalog(
{K: "displayName", V: jsonx.Str("TypeFerence source package: " + sourceName)},
{K: "type", V: jsonx.Str("application/vnd.typeference.source-package+json")},
{K: "description", V: jsonx.Str("Canonical typed source package for validation, audit, and reproducible compilation.")},
- {K: "version", V: jsonx.Str("1.0.0")},
+ {K: "version", V: jsonx.Str(sourceVersion)},
{K: "data", V: jsonx.Obj{
{K: "schemaVersion", V: jsonx.Num("1")},
{K: "digest", V: jsonx.Str(sourceDigest)},
@@ -354,6 +368,22 @@ func packageFiles(root string) (jsonx.Arr, error) {
return arr, nil
}
+func sourcePackageFiles(root string) (jsonx.Arr, error) {
+ files, err := packages.SourceFiles(root)
+ if err != nil {
+ return nil, err
+ }
+ arr := jsonx.Arr{}
+ for _, file := range files {
+ arr = append(arr, jsonx.Obj{
+ {K: "path", V: jsonx.Str(file.Path)},
+ {K: "mediaType", V: jsonx.Str(mediaType(file.Path))},
+ {K: "content", V: jsonx.Str(file.Content)},
+ })
+ }
+ return arr, nil
+}
+
func mediaType(path string) string {
switch strings.ToLower(filepath.Ext(path)) {
case ".json":
diff --git a/go/internal/compile/bundle.go b/go/internal/compile/bundle.go
index 6384bff..a5cffd5 100644
--- a/go/internal/compile/bundle.go
+++ b/go/internal/compile/bundle.go
@@ -1,6 +1,8 @@
package compile
import (
+ "sort"
+
"github.com/buchk/TypeFerence/go/internal/jsonx"
"github.com/buchk/TypeFerence/go/internal/resolve"
)
@@ -22,7 +24,7 @@ func bundleValue(agent *resolve.ResolvedAgent) jsonx.Value {
for _, skill := range agent.Skills {
skills = append(skills, skillValue(skill))
}
- return jsonx.Obj{
+ obj := jsonx.Obj{
{K: "id", V: jsonx.Str(agent.ID)},
{K: "displayName", V: jsonx.Str(agent.DisplayName)},
{K: "description", V: jsonx.Str(agent.Description)},
@@ -31,14 +33,39 @@ func bundleValue(agent *resolve.ResolvedAgent) jsonx.Value {
{K: "satisfies", V: stringArr(agent.Satisfies)},
{K: "slots", V: slots},
{K: "workingNorms", V: stringArr(agent.WorkingNorms)},
- {K: "contextFiles", V: stringArr(agent.ContextFiles)},
- {K: "skills", V: skills},
- {K: "provenance", V: provenanceValue(agent.Provenance)},
}
+ if len(agent.ContextFiles) > 0 {
+ obj = append(obj, jsonx.Member{K: "contextFiles", V: stringArr(agent.ContextFiles)})
+ }
+ // Typed context held by id. Absent when the agent holds none, so bundles
+ // that predate reference-by-id context are unchanged (ADR-0013).
+ if len(agent.ContextObjects) > 0 {
+ context := jsonx.Arr{}
+ for _, ref := range agent.ContextObjects {
+ values, err := jsonx.Parse(ref.ValuesJSON)
+ if err != nil {
+ values = jsonx.Obj{}
+ }
+ item := jsonx.Obj{
+ {K: "id", V: jsonx.Str(ref.ID)},
+ {K: "contextType", V: jsonx.Str(ref.ContextType)},
+ {K: "satisfies", V: stringArr(ref.Satisfies)},
+ }
+ if ref.ValuesJSON != "{}" {
+ item = append(item, jsonx.Member{K: "values", V: values})
+ }
+ item = append(item, jsonx.Member{K: "content", V: jsonx.Str(ref.Content)})
+ context = append(context, item)
+ }
+ obj = append(obj, jsonx.Member{K: "context", V: context})
+ }
+ obj = append(obj, jsonx.Member{K: "skills", V: skills})
+ obj = append(obj, jsonx.Member{K: "provenance", V: provenanceValue(agent.Provenance)})
+ return obj
}
func skillValue(skill resolve.ResolvedSkill) jsonx.Value {
- return jsonx.Obj{
+ obj := jsonx.Obj{
{K: "dispatchName", V: jsonx.Str(skill.DispatchName)},
{K: "capabilityId", V: jsonx.Str(skill.CapabilityID)},
{K: "implementationId", V: jsonx.Str(skill.ImplementationID)},
@@ -46,9 +73,49 @@ func skillValue(skill resolve.ResolvedSkill) jsonx.Value {
{K: "instructions", V: jsonx.Str(skill.Instructions)},
{K: "inputSchema", V: jsonx.Str(skill.InputSchema)},
{K: "outputSchema", V: jsonx.Str(skill.OutputSchema)},
- {K: "contextFiles", V: stringArr(skill.ContextFiles)},
- {K: "provenance", V: provenanceValue(skill.Provenance)},
}
+ if len(skill.ContextFiles) > 0 {
+ obj = append(obj, jsonx.Member{K: "contextFiles", V: stringArr(skill.ContextFiles)})
+ }
+ // A multimodal skill also emits its per-mode renderings. This member is
+ // absent for unimodal skills, so their bundle output is unchanged (ADR-0012).
+ if len(skill.Variants) > 0 {
+ modes := make([]string, 0, len(skill.Variants))
+ for mode := range skill.Variants {
+ modes = append(modes, mode)
+ }
+ sort.Strings(modes)
+ variants := jsonx.Obj{}
+ for _, mode := range modes {
+ variant := jsonx.Obj{
+ {K: "instructions", V: jsonx.Str(skill.Variants[mode])},
+ {K: "requiresContextTypes", V: stringArr(skill.VariantContextRequirements[mode])},
+ {K: "requiresTools", V: stringArr(skill.VariantToolRequirements[mode])},
+ }
+ variants = append(variants, jsonx.Member{K: mode, V: variant})
+ }
+ obj = append(obj, jsonx.Member{K: "variants", V: variants})
+ }
+ if len(skill.RequiresContextTypes) > 0 {
+ obj = append(obj, jsonx.Member{K: "requiresContextTypes", V: stringArr(skill.RequiresContextTypes)})
+ }
+ if len(skill.RequiresTools) > 0 {
+ obj = append(obj, jsonx.Member{K: "requiresTools", V: stringArr(skill.RequiresTools)})
+ }
+ if skill.Exposed {
+ obj = append(obj, jsonx.Member{K: "exposed", V: jsonx.Bool(true)})
+ }
+ // Modifier state is emitted only when a lock is engaged, so permissive
+ // bindings are unchanged. Absent means the axis is at its default — open,
+ // optional — which is what lets diff flag open -> sealed (ADR-0016).
+ if skill.Sealed {
+ obj = append(obj, jsonx.Member{K: "sealed", V: jsonx.Bool(true)})
+ }
+ if skill.Required {
+ obj = append(obj, jsonx.Member{K: "required", V: jsonx.Bool(true)})
+ }
+ obj = append(obj, jsonx.Member{K: "provenance", V: provenanceValue(skill.Provenance)})
+ return obj
}
// provenanceJSON renders the canonical indented provenance.json.
diff --git a/go/internal/compile/compiler.go b/go/internal/compile/compiler.go
index 5c530fc..5efdda3 100644
--- a/go/internal/compile/compiler.go
+++ b/go/internal/compile/compiler.go
@@ -9,6 +9,8 @@ import (
"sort"
"strings"
+ "github.com/buchk/TypeFerence/go/internal/jsonx"
+ "github.com/buchk/TypeFerence/go/internal/packages"
"github.com/buchk/TypeFerence/go/internal/resolve"
"github.com/buchk/TypeFerence/go/internal/resource"
"github.com/buchk/TypeFerence/go/internal/trust"
@@ -57,6 +59,10 @@ type ArdPublicationOptions struct {
// Validate resolves every agent in a source directory.
func Validate(source, trustConfigPath string) ([]*resolve.ResolvedAgent, error) {
+ return ValidateWithPackages(source, trustConfigPath, "")
+}
+
+func ValidateWithPackages(source, trustConfigPath, packagesDir string) ([]*resolve.ResolvedAgent, error) {
loaded, err := trust.Load(source, trustConfigPath)
if err != nil {
return nil, err
@@ -65,7 +71,7 @@ func Validate(source, trustConfigPath string) ([]*resolve.ResolvedAgent, error)
if loaded != nil {
trustPath = loaded.Path
}
- resources, err := resource.Load(source, trustPath)
+ resources, _, err := loadCompilationResources(source, trustPath, packagesDir, false)
if err != nil {
return nil, err
}
@@ -75,6 +81,17 @@ func Validate(source, trustConfigPath string) ([]*resolve.ResolvedAgent, error)
// Build compiles a source directory into the requested targets beneath
// output, returning the canonical sorted list of written files.
func Build(source, output string, targets []Target, ard *ArdPublicationOptions) ([]string, error) {
+ return BuildWithOptions(source, output, targets, ard, BuildOptions{})
+}
+
+type BuildOptions struct {
+ PackagesDir string
+ // LegacyV3 is reserved for reproducing historical conformance fixtures.
+ // CLI and package builds never expose it.
+ LegacyV3 bool
+}
+
+func BuildWithOptions(source, output string, targets []Target, ard *ArdPublicationOptions, options BuildOptions) ([]string, error) {
trustConfigPath := ""
if ard != nil {
trustConfigPath = ard.TrustConfigPath
@@ -87,10 +104,13 @@ func Build(source, output string, targets []Target, ard *ArdPublicationOptions)
if loaded != nil {
trustPath = loaded.Path
}
- resources, err := resource.Load(source, trustPath)
+ resources, lockedPackages, err := loadCompilationResources(source, trustPath, options.PackagesDir, options.LegacyV3)
if err != nil {
return nil, err
}
+ if _, err := resource.LoadProject(source); err != nil {
+ return nil, err
+ }
all, err := resolve.New(resources).ResolveAll()
if err != nil {
return nil, err
@@ -102,6 +122,14 @@ func Build(source, output string, targets []Target, ard *ArdPublicationOptions)
}
}
sort.Slice(agents, func(i, j int) bool { return agents[i].ID < agents[j].ID })
+ if err := validateAgentArtifactNames(agents); err != nil {
+ return nil, err
+ }
+ sourceDigest, err := HashSource(source)
+ if err != nil {
+ return nil, err
+ }
+ provenance := buildProvenance{SourceDigest: "sha256:" + sourceDigest, Dependencies: lockedPackages}
requested := distinctSortedTargets(targets)
if len(requested) == 0 {
@@ -111,6 +139,20 @@ func Build(source, output string, targets []Target, ard *ArdPublicationOptions)
if err != nil {
return nil, resource.Errorf("Invalid output directory: %s", output)
}
+ sourceRoot, err := filepath.Abs(source)
+ if err != nil {
+ return nil, resource.Errorf("Source directory not found: %s", source)
+ }
+ if samePath(root, sourceRoot) {
+ return nil, resource.Errorf("Output directory must not be the source root: %s", output)
+ }
+ if isBeneath(sourceRoot, root) {
+ relative, _ := filepath.Rel(sourceRoot, root)
+ first := strings.Split(filepath.ToSlash(relative), "/")[0]
+ if first != "dist" && first != "bin" && first != "obj" {
+ return nil, resource.Errorf("Output beneath the source root must be under dist, bin, or obj: %s", output)
+ }
+ }
if err := os.MkdirAll(root, 0o755); err != nil {
return nil, resource.Errorf("Cannot create output directory: %s", root)
}
@@ -124,10 +166,13 @@ func Build(source, output string, targets []Target, ard *ArdPublicationOptions)
return nil, resource.Errorf("Cannot create target directory: %s", targetRoot)
}
for _, agent := range agents {
- if err := writeTarget(target, targetRoot, agent, &written); err != nil {
+ if err := writeTarget(target, targetRoot, agent, provenance, &written); err != nil {
return nil, err
}
}
+ if err := writeBuildIndex(targetRoot, target, agents, provenance, &written); err != nil {
+ return nil, err
+ }
}
if ard != nil {
ardRoot := filepath.Join(root, "ard")
@@ -175,6 +220,36 @@ func Build(source, output string, targets []Target, ard *ArdPublicationOptions)
return written, nil
}
+func validateAgentArtifactNames(agents []*resolve.ResolvedAgent) error {
+ owners := map[string]string{}
+ for _, agent := range agents {
+ slug := resolve.Leaf(agent.ID)
+ if prior, duplicate := owners[slug]; duplicate {
+ return resource.Errorf("agents %s and %s produce the same target artifact path %s", prior, agent.ID, slug)
+ }
+ owners[slug] = agent.ID
+ }
+ return nil
+}
+
+func loadCompilationResources(source, trustPath, packagesDir string, legacyV3 bool) (map[string]*resource.Document, []packages.LockedPackage, error) {
+ rootResources, err := resource.LoadWithOptions(source, trustPath, resource.LoadOptions{AllowLegacyV3: legacyV3})
+ if err != nil {
+ return nil, nil, err
+ }
+ dependencies, locked, err := packages.LoadDependencies(source, packagesDir)
+ if err != nil {
+ return nil, nil, err
+ }
+ for id, document := range dependencies {
+ if _, exists := rootResources[id]; exists {
+ return nil, nil, resource.Errorf("root resource cannot shadow locked dependency resource: %s", id)
+ }
+ rootResources[id] = document
+ }
+ return rootResources, locked, nil
+}
+
func distinctSortedTargets(targets []Target) []Target {
seen := map[Target]bool{}
result := []Target{}
@@ -196,11 +271,62 @@ func isBeneath(root, path string) bool {
return strings.HasPrefix(path, prefix)
}
-func writeTarget(target Target, root string, agent *resolve.ResolvedAgent, written *[]string) error {
+func samePath(left, right string) bool {
+ if runtimeCaseInsensitive {
+ return strings.EqualFold(left, right)
+ }
+ return left == right
+}
+
+type buildProvenance struct {
+ SourceDigest string
+ Dependencies []packages.LockedPackage
+}
+
+func writeBuildIndex(root string, target Target, agents []*resolve.ResolvedAgent, provenance buildProvenance, written *[]string) error {
+ artifacts := jsonx.Arr{}
+ for _, agent := range agents {
+ slug := resolve.Leaf(agent.ID)
+ digest, err := HashDirectory(filepath.Join(root, slug))
+ if err != nil {
+ return err
+ }
+ artifacts = append(artifacts, jsonx.Obj{
+ {K: "agentId", V: jsonx.Str(agent.ID)},
+ {K: "path", V: jsonx.Str(slug)},
+ {K: "digest", V: jsonx.Str("sha256:" + digest)},
+ })
+ }
+ index := jsonx.Indented(jsonx.Obj{
+ {K: "schemaVersion", V: jsonx.Num("1")},
+ {K: "target", V: jsonx.Str(target.String())},
+ {K: "sourceDigest", V: jsonx.Str(provenance.SourceDigest)},
+ {K: "artifacts", V: artifacts},
+ }) + "\n"
+ return writeFile(filepath.Join(root, ".typeference", "build.json"), index, written)
+}
+
+func writeTarget(target Target, root string, agent *resolve.ResolvedAgent, provenance buildProvenance, written *[]string) error {
slug := resolve.Leaf(agent.ID)
+ linkPath := filepath.Join(root, slug, ".typeference", "link.json")
+ if err := validateTargetContext(target, agent); err != nil {
+ return err
+ }
+ if mode := variantForTarget(target); mode != "" {
+ for _, skill := range agent.Skills {
+ if len(skill.Variants) > 0 {
+ if _, exists := skill.Variants[mode]; !exists {
+ return resource.Errorf("%s target requires variant %q on multimodal skill %s",
+ target.String(), mode, skill.ImplementationID)
+ }
+ }
+ }
+ }
switch target {
case Neutral:
- if err := writeFile(filepath.Join(root, slug, "AGENTS.md"), renderInstructions(agent), written); err != nil {
+ // Canonical bundle: an index doc plus a SKILL.md per skill, fanning out
+ // one SKILL..md per variant (ADR-0012).
+ if err := writeFile(filepath.Join(root, slug, "AGENTS.md"), renderInstructions(agent, false, ""), written); err != nil {
return err
}
if err := writeFile(filepath.Join(root, slug, "bundle.json"), bundleJSON(agent)+"\n", written); err != nil {
@@ -210,31 +336,31 @@ func writeTarget(target Target, root string, agent *resolve.ResolvedAgent, writt
return err
}
for _, skill := range agent.Skills {
- if err := writeFile(filepath.Join(root, slug, "skills", skillSlug(skill), "SKILL.md"), renderSkill(skill), written); err != nil {
+ if err := writeSkillFiles(filepath.Join(root, slug, "skills", skillSlug(skill)), skill, "", true, written); err != nil {
return err
}
}
case Codex:
- if err := writeFile(filepath.Join(root, slug, "AGENTS.md"), renderInstructions(agent), written); err != nil {
+ // Interactive coding surface: an index doc plus one SKILL.md rendering the
+ // manual variant.
+ if err := writeFile(filepath.Join(root, slug, "AGENTS.md"), renderInstructions(agent, false, ""), written); err != nil {
return err
}
for _, skill := range agent.Skills {
- if err := writeFile(filepath.Join(root, slug, ".agents", "skills", skillSlug(skill), "SKILL.md"), renderSkill(skill), written); err != nil {
+ if err := writeSkillFiles(filepath.Join(root, slug, ".agents", "skills", skillSlug(skill)), skill, variantForTarget(target), false, written); err != nil {
return err
}
}
if err := writeFile(filepath.Join(root, slug, ".typeference", "bundle.json"), bundleJSON(agent)+"\n", written); err != nil {
return err
}
- config := "[mcp_servers.typeference]\ncommand = \"typeference\"\nargs = [\"serve\", \".typeference\"]\n"
- if err := writeFile(filepath.Join(root, slug, ".codex", "config.toml"), config, written); err != nil {
- return err
- }
case Copilot:
- if err := writeFile(filepath.Join(root, slug, ".github", "copilot-instructions.md"), renderInstructions(agent), written); err != nil {
+ // No per-skill file: instructions are inlined in the manual variant.
+ instructions := renderInstructions(agent, true, variantForTarget(target))
+ if err := writeFile(filepath.Join(root, slug, ".github", "copilot-instructions.md"), instructions, written); err != nil {
return err
}
- agentMD := "---\nname: " + slug + "\ndescription: " + escapeYAML(agent.Description) + "\n---\n\n" + renderInstructions(agent)
+ agentMD := "---\nname: " + slug + "\ndescription: " + escapeYAML(agent.Description) + "\n---\n\n" + instructions
if err := writeFile(filepath.Join(root, slug, ".github", "agents", slug+".agent.md"), agentMD, written); err != nil {
return err
}
@@ -242,10 +368,11 @@ func writeTarget(target Target, root string, agent *resolve.ResolvedAgent, writt
return err
}
case Cursor:
- if err := writeFile(filepath.Join(root, slug, "AGENTS.md"), renderInstructions(agent), written); err != nil {
+ instructions := renderInstructions(agent, true, variantForTarget(target))
+ if err := writeFile(filepath.Join(root, slug, "AGENTS.md"), instructions, written); err != nil {
return err
}
- rule := "---\ndescription: " + escapeYAML(agent.Description) + "\nglobs:\nalwaysApply: true\n---\n\n" + renderInstructions(agent)
+ rule := "---\ndescription: " + escapeYAML(agent.Description) + "\nglobs:\nalwaysApply: true\n---\n\n" + instructions
if err := writeFile(filepath.Join(root, slug, ".cursor", "rules", slug+".mdc"), rule, written); err != nil {
return err
}
@@ -253,10 +380,122 @@ func writeTarget(target Target, root string, agent *resolve.ResolvedAgent, writt
return err
}
}
+ return writeFile(linkPath, linkRequirementsJSON(agent, target, provenance)+"\n", written)
+}
+
+func validateTargetContext(target Target, agent *resolve.ResolvedAgent) error {
+ provided := map[string]bool{}
+ for _, context := range agent.ContextObjects {
+ for _, contextType := range context.Satisfies {
+ provided[contextType] = true
+ }
+ }
+ for _, skill := range agent.Skills {
+ for _, required := range skill.RequiresContextTypes {
+ if !provided[required] {
+ return resource.Errorf("%s: skill %s requires context type %s, which no held context provides",
+ agent.ID, skill.ImplementationID, required)
+ }
+ }
+ modes := sortedModes(skill.Variants)
+ if selected := variantForTarget(target); selected != "" {
+ modes = []string{selected}
+ }
+ for _, mode := range modes {
+ for _, required := range skill.VariantContextRequirements[mode] {
+ if !provided[required] {
+ return resource.Errorf("%s target: skill %s mode %s requires context type %s, which no held context provides",
+ target.String(), skill.ImplementationID, mode, required)
+ }
+ }
+ }
+ }
return nil
}
-func renderInstructions(agent *resolve.ResolvedAgent) string {
+func linkRequirementsJSON(agent *resolve.ResolvedAgent, target Target, provenance buildProvenance) string {
+ modeSet := map[string]bool{}
+ imports := jsonx.Arr{}
+ for _, skill := range agent.Skills {
+ for mode := range skill.Variants {
+ modeSet[mode] = true
+ }
+ for _, toolID := range skill.RequiresTools {
+ imports = append(imports, jsonx.Obj{
+ {K: "toolId", V: jsonx.Str(toolID)},
+ {K: "skillId", V: jsonx.Str(skill.ImplementationID)},
+ {K: "mode", V: jsonx.Str("*")},
+ })
+ }
+ for _, mode := range sortedModes(skill.Variants) {
+ for _, toolID := range skill.VariantToolRequirements[mode] {
+ imports = append(imports, jsonx.Obj{
+ {K: "toolId", V: jsonx.Str(toolID)},
+ {K: "skillId", V: jsonx.Str(skill.ImplementationID)},
+ {K: "mode", V: jsonx.Str(mode)},
+ })
+ }
+ }
+ }
+ modes := make([]string, 0, len(modeSet))
+ for mode := range modeSet {
+ modes = append(modes, mode)
+ }
+ sort.Strings(modes)
+ dependencies := jsonx.Arr{}
+ for _, dependency := range provenance.Dependencies {
+ dependencies = append(dependencies, jsonx.Obj{
+ {K: "name", V: jsonx.Str(dependency.Name)},
+ {K: "version", V: jsonx.Str(dependency.Version)},
+ {K: "digest", V: jsonx.Str(dependency.Digest)},
+ })
+ }
+ return jsonx.Indented(jsonx.Obj{
+ {K: "schemaVersion", V: jsonx.Num("1")},
+ {K: "agentId", V: jsonx.Str(agent.ID)},
+ {K: "target", V: jsonx.Str(target.String())},
+ {K: "defaultMode", V: jsonx.Str(variantForTarget(target))},
+ {K: "sourceDigest", V: jsonx.Str(provenance.SourceDigest)},
+ {K: "dependencies", V: dependencies},
+ {K: "modes", V: stringArr(modes)},
+ {K: "toolImports", V: imports},
+ })
+}
+
+// variantForTarget is the invocation-mode variant a target renders for a
+// multimodal skill: interactive/human surfaces get "manual"; the neutral
+// (canonical) bundle keeps the default and fans every variant out (ADR-0012).
+func variantForTarget(t Target) string {
+ switch t {
+ case Copilot, Cursor, Codex:
+ return "manual"
+ default:
+ return ""
+ }
+}
+
+// writeSkillFiles writes a skill's SKILL.md (rendered in the given variant, or
+// the default when variant is empty) and, when fanout is set, one SKILL..md
+// per variant (ADR-0012). Shared by every target that emits per-skill files.
+func writeSkillFiles(dir string, skill resolve.ResolvedSkill, variant string, fanout bool, written *[]string) error {
+ primary := skill.Instructions
+ if variant != "" {
+ primary = skill.InstructionsFor(variant)
+ }
+ if err := writeFile(filepath.Join(dir, "SKILL.md"), renderSkillWith(skill, primary), written); err != nil {
+ return err
+ }
+ if fanout {
+ for _, mode := range sortedModes(skill.Variants) {
+ if err := writeFile(filepath.Join(dir, "SKILL."+mode+".md"), renderSkillWith(skill, skill.Variants[mode]), written); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func renderInstructions(agent *resolve.ResolvedAgent, inlineInstructions bool, variant string) string {
var b strings.Builder
b.WriteString("# " + agent.DisplayName + "\n\n" + agent.Description + "\n\n")
if len(agent.WorkingNorms) > 0 {
@@ -273,24 +512,61 @@ func renderInstructions(agent *resolve.ResolvedAgent) string {
}
b.WriteString("\n")
}
+ // Held context objects are inlined so the compiled agent actually contains
+ // its context, not just a reference to it (ADR-0013).
+ if len(agent.ContextObjects) > 0 {
+ b.WriteString("## Context\n\n")
+ for _, ref := range agent.ContextObjects {
+ heading := ref.DisplayName
+ if strings.TrimSpace(heading) == "" {
+ heading = ref.ID
+ }
+ b.WriteString("### " + heading + "\n\n")
+ if content := strings.TrimSpace(ref.Content); content != "" {
+ b.WriteString(content + "\n\n")
+ }
+ }
+ }
b.WriteString("## Available skills\n\n")
for _, skill := range agent.Skills {
b.WriteString("- `" + skill.DispatchName + "`: " + skill.Description + "\n")
+ // Targets with no separate SKILL.md inline the instructions here, in the
+ // surface's variant, so those agents actually receive skill behavior
+ // rather than just a name (ADR-0012).
+ if inlineInstructions {
+ b.WriteString("\n" + strings.TrimSpace(skill.InstructionsFor(variant)) + "\n\n")
+ }
}
b.WriteString("\n")
return b.String()
}
-func renderSkill(skill resolve.ResolvedSkill) string {
+// renderSkillWith renders a skill's SKILL.md using the given instructions, so a
+// per-variant file can carry that mode's rendering (ADR-0012).
+func renderSkillWith(skill resolve.ResolvedSkill, instructions string) string {
+ base := "---\nname: " + skillSlug(skill) + "\ndescription: " + escapeYAML(skill.Description) + "\n---\n\n" +
+ strings.TrimSpace(instructions) + "\n"
+ if len(skill.ContextFiles) == 0 {
+ return base
+ }
lines := make([]string, len(skill.ContextFiles))
for i, file := range skill.ContextFiles {
lines[i] = "- `" + file + "`"
}
- return "---\nname: " + skillSlug(skill) + "\ndescription: " + escapeYAML(skill.Description) + "\n---\n\n" +
- strings.TrimSpace(skill.Instructions) + "\n\n## Context loaded on invocation\n\n" +
+ return base + "\n## Legacy context loaded on invocation\n\n" +
strings.Join(lines, "\n") + "\n"
}
+// sortedModes returns a variant map's mode names in canonical order.
+func sortedModes(variants map[string]string) []string {
+ modes := make([]string, 0, len(variants))
+ for mode := range variants {
+ modes = append(modes, mode)
+ }
+ sort.Strings(modes)
+ return modes
+}
+
func skillSlug(skill resolve.ResolvedSkill) string { return resolve.Leaf(skill.CapabilityID) }
func escapeYAML(value string) string {
diff --git a/go/internal/compile/context_emit_test.go b/go/internal/compile/context_emit_test.go
new file mode 100644
index 0000000..729f67f
--- /dev/null
+++ b/go/internal/compile/context_emit_test.go
@@ -0,0 +1,47 @@
+package compile
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func TestBundleEmitsHeldContext(t *testing.T) {
+ src := t.TempDir()
+ writeSrc(t, src, "ct.yaml", "schemaVersion: 4\nkind: contextType\nid: acme/ct/cast@1.0.0\nfields:\n owner:\n type: string\n required: true\n governed:\n type: boolean\n default: false\n")
+ writeSrc(t, src, "note.yaml", "schemaVersion: 4\nkind: context\nid: acme/notes/n@1.0.0\ncontextType: acme/ct/cast@1.0.0\nvalues:\n owner: Dana\n")
+ writeSrc(t, src, "agent.yaml", "schemaVersion: 4\nkind: agent\nid: acme/agent@1.0.0\ncontext:\n - acme/notes/n@1.0.0\n")
+ out := t.TempDir()
+ targets, _ := ParseTargets("neutral")
+ if _, err := Build(src, out, targets, nil); err != nil {
+ t.Fatal(err)
+ }
+ bundle, err := os.ReadFile(filepath.Join(out, "neutral", "agent", "bundle.json"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !strings.Contains(string(bundle), `"context"`) || !strings.Contains(string(bundle), "acme/notes/n@1.0.0") {
+ t.Errorf("bundle should list held context objects:\n%s", bundle)
+ }
+ if !strings.Contains(string(bundle), "acme/ct/cast@1.0.0") {
+ t.Errorf("held context should carry its contextType")
+ }
+ if !strings.Contains(string(bundle), `"owner": "Dana"`) || !strings.Contains(string(bundle), `"governed": false`) {
+ t.Errorf("bundle should preserve values and materialized defaults:\n%s", bundle)
+ }
+}
+
+func TestBundleOmitsContextWhenNoneHeld(t *testing.T) {
+ src := t.TempDir()
+ writeSrc(t, src, "agent.yaml", "schemaVersion: 4\nkind: agent\nid: acme/agent@1.0.0\n")
+ out := t.TempDir()
+ targets, _ := ParseTargets("neutral")
+ if _, err := Build(src, out, targets, nil); err != nil {
+ t.Fatal(err)
+ }
+ bundle, _ := os.ReadFile(filepath.Join(out, "neutral", "agent", "bundle.json"))
+ if strings.Contains(string(bundle), `"context"`) {
+ t.Errorf("an agent holding no context must not emit a context member:\n%s", bundle)
+ }
+}
diff --git a/go/internal/compile/deployment_test.go b/go/internal/compile/deployment_test.go
new file mode 100644
index 0000000..70a49eb
--- /dev/null
+++ b/go/internal/compile/deployment_test.go
@@ -0,0 +1,59 @@
+package compile
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func writeSrc(t *testing.T, root, name, content string) {
+ t.Helper()
+ if err := os.WriteFile(filepath.Join(root, name), []byte(content), 0o644); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestBuildIsUnlinked(t *testing.T) {
+ source := t.TempDir()
+ writeSrc(t, source, "agent.yaml", "schemaVersion: 4\nkind: agent\nid: acme/agents/a@1.0.0\n")
+ output := t.TempDir()
+ if _, err := Build(source, output, []Target{Codex}, nil); err != nil {
+ t.Fatal(err)
+ }
+ agentRoot := filepath.Join(output, "codex", "a")
+ if _, err := os.Stat(filepath.Join(agentRoot, ".codex", "config.toml")); !os.IsNotExist(err) {
+ t.Fatal("build must not emit active Codex runtime configuration")
+ }
+ if _, err := os.Stat(filepath.Join(agentRoot, ".typeference", "link.json")); err != nil {
+ t.Fatal("build must emit typed link requirements")
+ }
+}
+
+func TestProjectManifestRejectsDeployment(t *testing.T) {
+ source := t.TempDir()
+ writeSrc(t, source, "agent.yaml", "schemaVersion: 4\nkind: agent\nid: acme/agents/a@1.0.0\n")
+ writeSrc(t, source, "typeference.yaml", "schemaVersion: 2\nname: acme/agents\nversion: 1.0.0\ndeployment:\n mcpCommand: nope\n")
+ _, err := Build(source, t.TempDir(), []Target{Codex}, nil)
+ if err == nil || !strings.Contains(err.Error(), "deployment") {
+ t.Fatalf("deployment metadata in the source manifest must fail, got %v", err)
+ }
+}
+
+func TestARDDoesNotInventCallableCards(t *testing.T) {
+ source := t.TempDir()
+ writeSrc(t, source, "cap.yaml", "schemaVersion: 4\nkind: capability\nid: acme/capabilities/c@1.0.0\nvisibility: exposed\n")
+ writeSrc(t, source, "skill.yaml", "schemaVersion: 4\nkind: skill\nid: acme/skills/s@1.0.0\nbinds: acme/capabilities/c@1.0.0\ninstructions: do it\n")
+ writeSrc(t, source, "agent.yaml", "schemaVersion: 4\nkind: agent\nid: acme/agents/a@1.0.0\nskills:\n - ref: acme/skills/s@1.0.0\n")
+ output := t.TempDir()
+ if _, err := Build(source, output, []Target{Neutral}, &ArdPublicationOptions{PublisherDomain: "acme.example"}); err != nil {
+ t.Fatal(err)
+ }
+ catalog, err := os.ReadFile(filepath.Join(output, "ard", "ai-catalog.json"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if strings.Contains(string(catalog), "a2a-agent-card") || strings.Contains(string(catalog), "mcp-server") {
+ t.Fatal("unlinked build must not publish callable endpoint/provider claims")
+ }
+}
diff --git a/go/internal/compile/digest.go b/go/internal/compile/digest.go
index 0181acf..f405061 100644
--- a/go/internal/compile/digest.go
+++ b/go/internal/compile/digest.go
@@ -10,6 +10,7 @@ import (
"sort"
"strings"
+ "github.com/buchk/TypeFerence/go/internal/packages"
"github.com/buchk/TypeFerence/go/internal/resource"
)
@@ -37,6 +38,24 @@ func HashDirectory(directory string) (string, error) {
return hex.EncodeToString(h.Sum(nil)), nil
}
+// HashSource computes typeference-resource-set-v1 over the explicit source
+// membership used by pack. Generated output, caches, VCS data, and arbitrary
+// unreferenced files therefore cannot change source identity.
+func HashSource(source string) (string, error) {
+ files, err := packages.SourceFiles(source)
+ if err != nil {
+ return "", err
+ }
+ h := sha256.New()
+ for _, file := range files {
+ h.Write([]byte(file.Path))
+ h.Write([]byte{0})
+ h.Write([]byte(file.Content))
+ h.Write([]byte{0})
+ }
+ return hex.EncodeToString(h.Sum(nil)), nil
+}
+
// relativeFiles lists every file beneath root as forward-slash relative
// paths in canonical order.
func relativeFiles(root string) ([]string, error) {
diff --git a/go/internal/compile/fanout_test.go b/go/internal/compile/fanout_test.go
new file mode 100644
index 0000000..5ee454e
--- /dev/null
+++ b/go/internal/compile/fanout_test.go
@@ -0,0 +1,53 @@
+package compile
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func TestNeutralVariantFanout(t *testing.T) {
+ src := t.TempDir()
+ writeSrc(t, src, "cap.yaml", "schemaVersion: 4\nkind: capability\nid: acme/cap/c@1.0.0\n")
+ writeSrc(t, src, "skill.yaml", "schemaVersion: 4\nkind: skill\nid: acme/skills/s@1.0.0\nbinds: acme/cap/c@1.0.0\nvariants:\n pipeline:\n instructions: strict\n a2a:\n instructions: attributed\n")
+ writeSrc(t, src, "agent.yaml", "schemaVersion: 4\nkind: agent\nid: acme/agent@1.0.0\nskills:\n - ref: acme/skills/s@1.0.0\n")
+ out := t.TempDir()
+ targets, err := ParseTargets("neutral")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err := Build(src, out, targets, nil); err != nil {
+ t.Fatal(err)
+ }
+ base := filepath.Join(out, "neutral", "agent", "skills", "c")
+ for _, name := range []string{"SKILL.md", "SKILL.pipeline.md", "SKILL.a2a.md"} {
+ if _, err := os.Stat(filepath.Join(base, name)); err != nil {
+ t.Errorf("expected %s to be emitted: %v", name, err)
+ }
+ }
+ body, err := os.ReadFile(filepath.Join(base, "SKILL.a2a.md"))
+ if err != nil || !strings.Contains(string(body), "attributed") {
+ t.Errorf("SKILL.a2a.md should carry the a2a variant's instructions")
+ }
+}
+
+func TestUnimodalSkillNoFanout(t *testing.T) {
+ src := t.TempDir()
+ writeSrc(t, src, "cap.yaml", "schemaVersion: 4\nkind: capability\nid: acme/cap/c@1.0.0\n")
+ writeSrc(t, src, "skill.yaml", "schemaVersion: 4\nkind: skill\nid: acme/skills/s@1.0.0\nbinds: acme/cap/c@1.0.0\ninstructions: do it\n")
+ writeSrc(t, src, "agent.yaml", "schemaVersion: 4\nkind: agent\nid: acme/agent@1.0.0\nskills:\n - ref: acme/skills/s@1.0.0\n")
+ out := t.TempDir()
+ targets, _ := ParseTargets("neutral")
+ if _, err := Build(src, out, targets, nil); err != nil {
+ t.Fatal(err)
+ }
+ base := filepath.Join(out, "neutral", "agent", "skills", "c")
+ entries, err := os.ReadDir(base)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(entries) != 1 || entries[0].Name() != "SKILL.md" {
+ t.Errorf("unimodal skill should emit only SKILL.md, got %v", entries)
+ }
+}
diff --git a/go/internal/compile/parity_test.go b/go/internal/compile/parity_test.go
index 43afa9f..541951f 100644
--- a/go/internal/compile/parity_test.go
+++ b/go/internal/compile/parity_test.go
@@ -17,11 +17,10 @@ func repoPath(t *testing.T, parts ...string) string {
return filepath.Join(append([]string{root}, parts...)...)
}
-// TestHelioParityWithReferenceImplementation compiles the shared example and
-// byte-compares every target tree against the committed output of the C#
-// reference implementation. This is the cross-implementation contract: same
-// input, byte-identical artifacts.
-func TestHelioParityWithReferenceImplementation(t *testing.T) {
+// TestHelioParityWithCommittedOutput compiles the shared example and
+// byte-compares every target tree against the committed dist/ reference. This is
+// the determinism contract: same input, byte-identical artifacts (ADR-0014).
+func TestHelioParityWithCommittedOutput(t *testing.T) {
source := repoPath(t, "examples", "helio")
if _, err := os.Stat(source); err != nil {
t.Skipf("examples/helio not available: %v", err)
diff --git a/go/internal/compile/source_digest_test.go b/go/internal/compile/source_digest_test.go
new file mode 100644
index 0000000..826633d
--- /dev/null
+++ b/go/internal/compile/source_digest_test.go
@@ -0,0 +1,38 @@
+package compile
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func TestSourceDigestIgnoresGeneratedAndUnreferencedFiles(t *testing.T) {
+ source := t.TempDir()
+ writeSrc(t, source, "agent.yaml", "schemaVersion: 4\nkind: agent\nid: acme/agents/a@1.0.0\n")
+ before, err := HashSource(source)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := os.MkdirAll(filepath.Join(source, "dist"), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ writeSrc(t, filepath.Join(source, "dist"), "generated.json", "{}")
+ writeSrc(t, source, "notes.txt", "not a resource")
+ after, err := HashSource(source)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if before != after {
+ t.Fatalf("generated/unreferenced files changed source identity: %s != %s", before, after)
+ }
+}
+
+func TestBuildRejectsSourceRootAsOutput(t *testing.T) {
+ source := t.TempDir()
+ writeSrc(t, source, "agent.yaml", "schemaVersion: 4\nkind: agent\nid: acme/agents/a@1.0.0\n")
+ if _, err := Build(source, source, []Target{Neutral}, nil); err == nil ||
+ !strings.Contains(err.Error(), "must not be the source root") {
+ t.Fatalf("expected source-root output rejection, got %v", err)
+ }
+}
diff --git a/go/internal/compile/target_context_test.go b/go/internal/compile/target_context_test.go
new file mode 100644
index 0000000..8f391c0
--- /dev/null
+++ b/go/internal/compile/target_context_test.go
@@ -0,0 +1,54 @@
+package compile
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func TestTargetValidatesOnlyMaterializedVariantContext(t *testing.T) {
+ source := t.TempDir()
+ writeTargetContextFile(t, filepath.Join(source, "cap.yaml"), "schemaVersion: 4\nkind: capability\nid: acme/capabilities/c@1.0.0\n")
+ writeTargetContextFile(t, filepath.Join(source, "ct.yaml"), "schemaVersion: 4\nkind: contextType\nid: acme/context-types/runtime@1.0.0\n")
+ writeTargetContextFile(t, filepath.Join(source, "skill.yaml"), "schemaVersion: 4\nkind: skill\nid: acme/skills/s@1.0.0\nbinds: acme/capabilities/c@1.0.0\nvariants:\n manual:\n instructions: interactive\n a2a:\n instructions: remote\n requiresContextTypes: [acme/context-types/runtime@1.0.0]\n")
+ writeTargetContextFile(t, filepath.Join(source, "agent.yaml"), "schemaVersion: 4\nkind: agent\nid: acme/agents/a@1.0.0\nskills:\n - ref: acme/skills/s@1.0.0\n")
+
+ if _, err := Build(source, t.TempDir(), []Target{Codex}, nil); err != nil {
+ t.Fatalf("Codex materializes manual and must ignore a2a-only context: %v", err)
+ }
+ if _, err := Build(source, t.TempDir(), []Target{Neutral}, nil); err == nil ||
+ !strings.Contains(err.Error(), "a2a requires context type") {
+ t.Fatalf("neutral carries every mode and must reject missing a2a context, got %v", err)
+ }
+}
+
+func TestTargetNativeNameCollisionsFailClosed(t *testing.T) {
+ source := t.TempDir()
+ writeTargetContextFile(t, filepath.Join(source, "a.yaml"), "schemaVersion: 4\nkind: agent\nid: acme/agents/worker@1.0.0\n")
+ writeTargetContextFile(t, filepath.Join(source, "b.yaml"), "schemaVersion: 4\nkind: agent\nid: other/agents/worker@1.0.0\n")
+ if _, err := Build(source, t.TempDir(), []Target{Neutral}, nil); err == nil ||
+ !strings.Contains(err.Error(), "same target artifact path") {
+ t.Fatalf("expected colliding agent artifact names to fail, got %v", err)
+ }
+}
+
+func TestTargetDispatchNameCollisionsFailClosed(t *testing.T) {
+ source := t.TempDir()
+ writeTargetContextFile(t, filepath.Join(source, "cap-a.yaml"), "schemaVersion: 4\nkind: capability\nid: acme/capabilities/status@1.0.0\n")
+ writeTargetContextFile(t, filepath.Join(source, "cap-b.yaml"), "schemaVersion: 4\nkind: capability\nid: other/capabilities/status@1.0.0\n")
+ writeTargetContextFile(t, filepath.Join(source, "skill-a.yaml"), "schemaVersion: 4\nkind: skill\nid: acme/skills/status@1.0.0\nbinds: acme/capabilities/status@1.0.0\ninstructions: a\n")
+ writeTargetContextFile(t, filepath.Join(source, "skill-b.yaml"), "schemaVersion: 4\nkind: skill\nid: other/skills/status@1.0.0\nbinds: other/capabilities/status@1.0.0\ninstructions: b\n")
+ writeTargetContextFile(t, filepath.Join(source, "agent.yaml"), "schemaVersion: 4\nkind: agent\nid: acme/agents/worker@1.0.0\nskills:\n - ref: acme/skills/status@1.0.0\n - ref: other/skills/status@1.0.0\n")
+ if _, err := Build(source, t.TempDir(), []Target{Neutral}, nil); err == nil ||
+ !strings.Contains(err.Error(), "same target dispatch name") {
+ t.Fatalf("expected colliding dispatch names to fail, got %v", err)
+ }
+}
+
+func writeTargetContextFile(t *testing.T, path, content string) {
+ t.Helper()
+ if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/go/internal/compile/target_variant_test.go b/go/internal/compile/target_variant_test.go
new file mode 100644
index 0000000..340c6d5
--- /dev/null
+++ b/go/internal/compile/target_variant_test.go
@@ -0,0 +1,51 @@
+package compile
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func TestTargetsSelectSurfaceVariant(t *testing.T) {
+ src := t.TempDir()
+ writeSrc(t, src, "cap.yaml", "schemaVersion: 4\nkind: capability\nid: acme/cap/c@1.0.0\n")
+ writeSrc(t, src, "skill.yaml", "schemaVersion: 4\nkind: skill\nid: acme/skills/s@1.0.0\nbinds: acme/cap/c@1.0.0\nvariants:\n pipeline:\n instructions: PIPELINE_TEXT\n manual:\n instructions: MANUAL_TEXT\n")
+ writeSrc(t, src, "agent.yaml", "schemaVersion: 4\nkind: agent\nid: acme/agent@1.0.0\nskills:\n - ref: acme/skills/s@1.0.0\n")
+ out := t.TempDir()
+ targets, err := ParseTargets("all")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err := Build(src, out, targets, nil); err != nil {
+ t.Fatal(err)
+ }
+ read := func(parts ...string) string {
+ b, _ := os.ReadFile(filepath.Join(append([]string{out}, parts...)...))
+ return string(b)
+ }
+
+ // Copilot has no per-skill file, so it inlines the manual variant.
+ cop := read("copilot", "agent", ".github", "copilot-instructions.md")
+ if !strings.Contains(cop, "MANUAL_TEXT") || strings.Contains(cop, "PIPELINE_TEXT") {
+ t.Errorf("copilot should inline the manual variant, not pipeline:\n%s", cop)
+ }
+ // Cursor likewise inlines manual.
+ cur := read("cursor", "agent", "AGENTS.md")
+ if !strings.Contains(cur, "MANUAL_TEXT") {
+ t.Errorf("cursor should inline the manual variant")
+ }
+ // Codex's SKILL.md renders the manual variant.
+ cod := read("codex", "agent", ".agents", "skills", "c", "SKILL.md")
+ if !strings.Contains(cod, "MANUAL_TEXT") || strings.Contains(cod, "PIPELINE_TEXT") {
+ t.Errorf("codex SKILL.md should render the manual variant")
+ }
+ // Neutral's SKILL.md keeps the default (pipeline-preferred) and fans out.
+ neu := read("neutral", "agent", "skills", "c", "SKILL.md")
+ if !strings.Contains(neu, "PIPELINE_TEXT") {
+ t.Errorf("neutral SKILL.md should render the default (pipeline) variant")
+ }
+ if !strings.Contains(read("neutral", "agent", "skills", "c", "SKILL.manual.md"), "MANUAL_TEXT") {
+ t.Errorf("neutral should still fan out SKILL.manual.md")
+ }
+}
diff --git a/go/internal/compile/variants_test.go b/go/internal/compile/variants_test.go
new file mode 100644
index 0000000..b083a8f
--- /dev/null
+++ b/go/internal/compile/variants_test.go
@@ -0,0 +1,30 @@
+package compile
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/buchk/TypeFerence/go/internal/jsonx"
+ "github.com/buchk/TypeFerence/go/internal/resolve"
+)
+
+func TestSkillValueOmitsVariantsWhenUnimodal(t *testing.T) {
+ out := jsonx.Indented(skillValue(resolve.ResolvedSkill{DispatchName: "a.b", Instructions: "x"}))
+ if strings.Contains(out, "variants") {
+ t.Errorf("a unimodal skill must not emit a variants member:\n%s", out)
+ }
+}
+
+func TestSkillValueEmitsVariantsWhenMultimodal(t *testing.T) {
+ out := jsonx.Indented(skillValue(resolve.ResolvedSkill{
+ DispatchName: "a.b",
+ Instructions: "strict",
+ Variants: map[string]string{"pipeline": "strict", "manual": "explain"},
+ }))
+ if !strings.Contains(out, `"variants"`) {
+ t.Errorf("a multimodal skill must emit a variants member:\n%s", out)
+ }
+ if !strings.Contains(out, "explain") {
+ t.Errorf("variants member must contain each mode's instructions:\n%s", out)
+ }
+}
diff --git a/go/internal/deploy/deploy.go b/go/internal/deploy/deploy.go
new file mode 100644
index 0000000..c93b68f
--- /dev/null
+++ b/go/internal/deploy/deploy.go
@@ -0,0 +1,709 @@
+// Package deploy validates external deployment bindings and materializes
+// runnable target-native configuration from deterministic unlinked builds.
+package deploy
+
+import (
+ "bytes"
+ "crypto/sha256"
+ "encoding/hex"
+ "encoding/json"
+ "io/fs"
+ "net/url"
+ "os"
+ "path/filepath"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+
+ "github.com/buchk/TypeFerence/go/internal/compile"
+ "github.com/buchk/TypeFerence/go/internal/jsonx"
+ "github.com/buchk/TypeFerence/go/internal/resource"
+ "gopkg.in/yaml.v3"
+)
+
+var deploymentName = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._-]*$`)
+
+type ArtifactBinding struct {
+ Modes []string `yaml:"modes"`
+}
+
+type EnvironmentReference struct {
+ FromEnvironment string `yaml:"fromEnvironment"`
+}
+
+type Provider struct {
+ Kind string `yaml:"kind"`
+ Transport string `yaml:"transport"`
+ Command string `yaml:"command"`
+ Args []string `yaml:"args"`
+ URL string `yaml:"url"`
+ BearerTokenEnvironment string `yaml:"bearerTokenEnvironment"`
+ Environment map[string]EnvironmentReference `yaml:"environment"`
+}
+
+type ToolBinding struct {
+ Provider string `yaml:"provider"`
+ RemoteName string `yaml:"remoteName"`
+}
+
+type AgentEndpoint struct {
+ A2AURL string `yaml:"a2aUrl"`
+}
+
+type File struct {
+ SchemaVersion int `yaml:"schemaVersion"`
+ Environment string `yaml:"environment"`
+ Artifacts map[string]ArtifactBinding `yaml:"artifacts"`
+ Providers map[string]Provider `yaml:"providers"`
+ ToolBindings map[string]ToolBinding `yaml:"toolBindings"`
+ AgentEndpoints map[string]AgentEndpoint `yaml:"agentEndpoints"`
+}
+
+type requirements struct {
+ SchemaVersion int `json:"schemaVersion"`
+ AgentID string `json:"agentId"`
+ Target string `json:"target"`
+ DefaultMode string `json:"defaultMode"`
+ SourceDigest string `json:"sourceDigest"`
+ Dependencies []struct {
+ Name string `json:"name"`
+ Version string `json:"version"`
+ Digest string `json:"digest"`
+ } `json:"dependencies"`
+ Modes []string `json:"modes"`
+ ToolImports []struct {
+ ToolID string `json:"toolId"`
+ SkillID string `json:"skillId"`
+ Mode string `json:"mode"`
+ } `json:"toolImports"`
+ path string
+ slug string
+}
+
+type buildIndex struct {
+ SchemaVersion int `json:"schemaVersion"`
+ Target string `json:"target"`
+ SourceDigest string `json:"sourceDigest"`
+ Artifacts []struct {
+ AgentID string `json:"agentId"`
+ Path string `json:"path"`
+ Digest string `json:"digest"`
+ } `json:"artifacts"`
+}
+
+func Load(path string) (*File, []byte, error) {
+ data, err := os.ReadFile(path)
+ if err != nil {
+ return nil, nil, resource.Errorf("Cannot read deployment file: %s", path)
+ }
+ var deployment File
+ decoder := yaml.NewDecoder(bytes.NewReader(data))
+ decoder.KnownFields(true)
+ if err := decoder.Decode(&deployment); err != nil {
+ return nil, nil, resource.Errorf("Invalid deployment file: %s", err)
+ }
+ if deployment.SchemaVersion != 1 {
+ return nil, nil, resource.Errorf("deployment schemaVersion must be 1")
+ }
+ if strings.TrimSpace(deployment.Environment) == "" {
+ return nil, nil, resource.Errorf("deployment must name its environment")
+ }
+ for agent, artifact := range deployment.Artifacts {
+ if !resource.IsResourceID(agent) {
+ return nil, nil, resource.Errorf("invalid deployment artifact id: %s", agent)
+ }
+ seenModes := map[string]bool{}
+ for _, mode := range artifact.Modes {
+ if !deploymentName.MatchString(mode) {
+ return nil, nil, resource.Errorf("artifact %s has invalid mode %q", agent, mode)
+ }
+ if seenModes[mode] {
+ return nil, nil, resource.Errorf("artifact %s selects mode %s more than once", agent, mode)
+ }
+ seenModes[mode] = true
+ }
+ }
+ for name, provider := range deployment.Providers {
+ if !deploymentName.MatchString(name) || provider.Kind != "mcp" {
+ return nil, nil, resource.Errorf("provider %q must be a named MCP provider", name)
+ }
+ switch provider.Transport {
+ case "stdio":
+ if strings.TrimSpace(provider.Command) == "" || provider.URL != "" || provider.BearerTokenEnvironment != "" {
+ return nil, nil, resource.Errorf("stdio provider %s requires command and cannot set url", name)
+ }
+ case "http":
+ if provider.Command != "" || len(provider.Args) != 0 || len(provider.Environment) != 0 {
+ return nil, nil, resource.Errorf("http provider %s uses url, not command/args", name)
+ }
+ if err := validateHTTPS(provider.URL, "provider "+name+" url"); err != nil {
+ return nil, nil, err
+ }
+ default:
+ return nil, nil, resource.Errorf("provider %s transport must be stdio or http", name)
+ }
+ for variable, reference := range provider.Environment {
+ if strings.TrimSpace(variable) == "" || strings.TrimSpace(reference.FromEnvironment) == "" {
+ return nil, nil, resource.Errorf("provider %s environment bindings must reference a host environment variable", name)
+ }
+ if variable != reference.FromEnvironment {
+ return nil, nil, resource.Errorf("provider %s cannot rename environment variable %s to %s in deployment schemaVersion 1",
+ name, reference.FromEnvironment, variable)
+ }
+ }
+ }
+ for toolID, binding := range deployment.ToolBindings {
+ if !resource.IsResourceID(toolID) {
+ return nil, nil, resource.Errorf("invalid tool binding id: %s", toolID)
+ }
+ if !deploymentName.MatchString(binding.Provider) || strings.TrimSpace(binding.RemoteName) == "" {
+ return nil, nil, resource.Errorf("tool %s binding must name a provider and remote tool", toolID)
+ }
+ }
+ for agent, endpoint := range deployment.AgentEndpoints {
+ if !resource.IsResourceID(agent) {
+ return nil, nil, resource.Errorf("invalid agent endpoint id: %s", agent)
+ }
+ if endpoint.A2AURL == "" {
+ return nil, nil, resource.Errorf("agent endpoint %s must declare a2aUrl", agent)
+ }
+ if err := validateHTTPS(endpoint.A2AURL, "agent endpoint "+agent); err != nil {
+ return nil, nil, err
+ }
+ }
+ return &deployment, data, nil
+}
+
+func Link(input, deploymentPath, output string) ([]string, error) {
+ deployment, deploymentBytes, err := Load(deploymentPath)
+ if err != nil {
+ return nil, err
+ }
+ inputAbs, err := filepath.Abs(input)
+ if err != nil {
+ return nil, resource.Errorf("Invalid built target path: %s", input)
+ }
+ outputAbs, err := filepath.Abs(output)
+ if err != nil || sameOrWithin(inputAbs, outputAbs) || sameOrWithin(outputAbs, inputAbs) {
+ return nil, resource.Errorf("linked output must be a separate sibling of the unlinked input")
+ }
+ info, err := os.Stat(inputAbs)
+ if err != nil || !info.IsDir() {
+ return nil, resource.Errorf("Built target directory not found: %s", input)
+ }
+ reqs, err := loadRequirements(inputAbs)
+ if err != nil {
+ return nil, err
+ }
+ if len(reqs) == 0 {
+ return nil, resource.Errorf("No .typeference/link.json manifests found under %s", input)
+ }
+ if err := verifyBuildIndex(inputAbs, reqs); err != nil {
+ return nil, err
+ }
+ for _, req := range reqs {
+ if err := validateBindings(req, deployment); err != nil {
+ return nil, err
+ }
+ }
+ inputDigest, err := compile.HashDirectory(inputAbs)
+ if err != nil {
+ return nil, err
+ }
+ if err := os.RemoveAll(outputAbs); err != nil {
+ return nil, resource.Errorf("Cannot reset linked output: %s", outputAbs)
+ }
+ written, err := copyTree(inputAbs, outputAbs)
+ if err != nil {
+ return nil, err
+ }
+ buildIndexSource := filepath.Join(inputAbs, ".typeference", "build.json")
+ buildIndexBytes, err := os.ReadFile(buildIndexSource)
+ if err != nil {
+ return nil, resource.Errorf("Cannot preserve unlinked build index: %s", buildIndexSource)
+ }
+ copiedBuildIndex := filepath.Join(outputAbs, ".typeference", "build.json")
+ if err := os.Remove(copiedBuildIndex); err != nil {
+ return nil, resource.Errorf("Cannot replace copied build index: %s", copiedBuildIndex)
+ }
+ written = withoutPath(written, copiedBuildIndex)
+ unlinkedIndexPath := filepath.Join(outputAbs, ".typeference", "unlinked-build.json")
+ if err := write(unlinkedIndexPath, string(buildIndexBytes)); err != nil {
+ return nil, err
+ }
+ written = append(written, unlinkedIndexPath)
+ for _, req := range reqs {
+ bindings := toolBindingsJSON(req, deployment)
+ bindingsPath := filepath.Join(outputAbs, req.slug, ".typeference", "tool-bindings.json")
+ if err := write(bindingsPath, bindings); err != nil {
+ return nil, err
+ }
+ written = append(written, bindingsPath)
+ if req.Target == "codex" {
+ config, err := codexConfig(req, deployment)
+ if err != nil {
+ return nil, err
+ }
+ if config != "" {
+ path := filepath.Join(outputAbs, req.slug, ".codex", "config.toml")
+ if err := write(path, config); err != nil {
+ return nil, err
+ }
+ written = append(written, path)
+ }
+ }
+ if endpoint := deployment.AgentEndpoints[req.AgentID].A2AURL; endpoint != "" {
+ card, err := a2aCard(inputAbs, req, endpoint)
+ if err != nil {
+ return nil, err
+ }
+ path := filepath.Join(outputAbs, req.slug, ".typeference", "a2a-agent-card.json")
+ if err := write(path, card); err != nil {
+ return nil, err
+ }
+ written = append(written, path)
+ }
+ }
+ deploymentDigest := sha256.Sum256(bytes.ReplaceAll(deploymentBytes, []byte("\r\n"), []byte("\n")))
+ artifacts := jsonx.Arr{}
+ for _, req := range reqs {
+ digest, err := compile.HashDirectory(filepath.Join(outputAbs, req.slug))
+ if err != nil {
+ return nil, err
+ }
+ artifacts = append(artifacts, jsonx.Obj{
+ {K: "agentId", V: jsonx.Str(req.AgentID)},
+ {K: "path", V: jsonx.Str(req.slug)},
+ {K: "digest", V: jsonx.Str("sha256:" + digest)},
+ })
+ }
+ provenance := jsonx.Indented(jsonx.Obj{
+ {K: "schemaVersion", V: jsonx.Num("1")},
+ {K: "environment", V: jsonx.Str(deployment.Environment)},
+ {K: "unlinkedDigest", V: jsonx.Str("sha256:" + inputDigest)},
+ {K: "deploymentDigest", V: jsonx.Str("sha256:" + hex.EncodeToString(deploymentDigest[:]))},
+ {K: "artifacts", V: artifacts},
+ }) + "\n"
+ provenancePath := filepath.Join(outputAbs, ".typeference", "link-provenance.json")
+ if err := write(provenancePath, provenance); err != nil {
+ return nil, err
+ }
+ written = append(written, provenancePath)
+ sort.Strings(written)
+ return written, nil
+}
+
+func withoutPath(paths []string, removed string) []string {
+ result := paths[:0]
+ for _, path := range paths {
+ if path != removed {
+ result = append(result, path)
+ }
+ }
+ return result
+}
+
+func sameOrWithin(parent, child string) bool {
+ relative, err := filepath.Rel(parent, child)
+ return err == nil && (relative == "." ||
+ (!filepath.IsAbs(relative) && relative != ".." && !strings.HasPrefix(relative, ".."+string(filepath.Separator))))
+}
+
+func verifyBuildIndex(root string, reqs []requirements) error {
+ path := filepath.Join(root, ".typeference", "build.json")
+ data, err := os.ReadFile(path)
+ if err != nil {
+ return resource.Errorf("Built target is missing its integrity index: %s", path)
+ }
+ var index buildIndex
+ decoder := json.NewDecoder(bytes.NewReader(data))
+ decoder.DisallowUnknownFields()
+ if err := decoder.Decode(&index); err != nil || index.SchemaVersion != 1 {
+ return resource.Errorf("Invalid build integrity index: %s", path)
+ }
+ type expectedArtifact struct {
+ path string
+ digest string
+ }
+ expected := map[string]expectedArtifact{}
+ for _, artifact := range index.Artifacts {
+ if artifact.Path == "" || filepath.Base(filepath.Clean(artifact.Path)) != artifact.Path ||
+ !resource.IsResourceID(artifact.AgentID) || len(artifact.Digest) != 71 ||
+ !strings.HasPrefix(artifact.Digest, "sha256:") {
+ return resource.Errorf("Invalid artifact entry in build integrity index: %s", path)
+ }
+ if _, err := hex.DecodeString(strings.TrimPrefix(artifact.Digest, "sha256:")); err != nil {
+ return resource.Errorf("Invalid artifact digest in build integrity index: %s", path)
+ }
+ if _, exists := expected[artifact.AgentID]; exists {
+ return resource.Errorf("Duplicate artifact %s in build integrity index", artifact.AgentID)
+ }
+ expected[artifact.AgentID] = expectedArtifact{path: artifact.Path, digest: artifact.Digest}
+ }
+ if len(expected) != len(reqs) {
+ return resource.Errorf("Build integrity index does not match its link manifests")
+ }
+ for _, req := range reqs {
+ if req.Target != index.Target || req.SourceDigest != index.SourceDigest {
+ return resource.Errorf("Link requirements for %s do not match the build integrity index", req.AgentID)
+ }
+ want, ok := expected[req.AgentID]
+ if !ok {
+ return resource.Errorf("Build integrity index does not include %s", req.AgentID)
+ }
+ if want.path != req.slug {
+ return resource.Errorf("Build integrity index path does not match %s", req.AgentID)
+ }
+ digest, err := compile.HashDirectory(filepath.Join(root, req.slug))
+ if err != nil {
+ return err
+ }
+ if want.digest != "sha256:"+digest {
+ return resource.Errorf("Unlinked artifact digest mismatch for %s: expected %s, got sha256:%s",
+ req.AgentID, want.digest, digest)
+ }
+ }
+ return nil
+}
+
+func toolBindingsJSON(req requirements, deployment *File) string {
+ selected := map[string]bool{}
+ for _, mode := range deployment.Artifacts[req.AgentID].Modes {
+ selected[mode] = true
+ }
+ imports := append([]struct {
+ ToolID string `json:"toolId"`
+ SkillID string `json:"skillId"`
+ Mode string `json:"mode"`
+ }{}, req.ToolImports...)
+ sort.Slice(imports, func(i, j int) bool {
+ if imports[i].ToolID != imports[j].ToolID {
+ return imports[i].ToolID < imports[j].ToolID
+ }
+ if imports[i].SkillID != imports[j].SkillID {
+ return imports[i].SkillID < imports[j].SkillID
+ }
+ return imports[i].Mode < imports[j].Mode
+ })
+ bindings := jsonx.Arr{}
+ for _, imported := range imports {
+ if imported.Mode != "*" && !selected[imported.Mode] {
+ continue
+ }
+ binding := deployment.ToolBindings[imported.ToolID]
+ bindings = append(bindings, jsonx.Obj{
+ {K: "toolId", V: jsonx.Str(imported.ToolID)},
+ {K: "skillId", V: jsonx.Str(imported.SkillID)},
+ {K: "mode", V: jsonx.Str(imported.Mode)},
+ {K: "provider", V: jsonx.Str(binding.Provider)},
+ {K: "remoteName", V: jsonx.Str(binding.RemoteName)},
+ })
+ }
+ return jsonx.Indented(jsonx.Obj{
+ {K: "schemaVersion", V: jsonx.Num("1")},
+ {K: "environment", V: jsonx.Str(deployment.Environment)},
+ {K: "bindings", V: bindings},
+ }) + "\n"
+}
+
+func a2aCard(inputRoot string, req requirements, endpoint string) (string, error) {
+ bundlePath := filepath.Join(inputRoot, req.slug, ".typeference", "bundle.json")
+ if req.Target == "neutral" {
+ bundlePath = filepath.Join(inputRoot, req.slug, "bundle.json")
+ }
+ data, err := os.ReadFile(bundlePath)
+ if err != nil {
+ return "", resource.Errorf("Cannot read bundle for A2A card: %s", req.AgentID)
+ }
+ var bundle struct {
+ DisplayName string `json:"displayName"`
+ Description string `json:"description"`
+ Skills []struct {
+ DispatchName string `json:"dispatchName"`
+ CapabilityID string `json:"capabilityId"`
+ Description string `json:"description"`
+ Exposed bool `json:"exposed"`
+ Variants map[string]json.RawMessage `json:"variants"`
+ } `json:"skills"`
+ }
+ if err := json.Unmarshal(data, &bundle); err != nil {
+ return "", resource.Errorf("Invalid bundle for A2A card: %s", req.AgentID)
+ }
+ skills := jsonx.Arr{}
+ for _, skill := range bundle.Skills {
+ if !skill.Exposed {
+ continue
+ }
+ if len(skill.Variants) > 0 {
+ if _, supportsA2A := skill.Variants["a2a"]; !supportsA2A {
+ continue
+ }
+ }
+ skills = append(skills, jsonx.Obj{
+ {K: "id", V: jsonx.Str(skill.DispatchName)},
+ {K: "name", V: jsonx.Str(resourceName(skill.CapabilityID))},
+ {K: "description", V: jsonx.Str(skill.Description)},
+ {K: "tags", V: jsonx.Arr{jsonx.Str("typeference")}},
+ })
+ }
+ if len(skills) == 0 {
+ return "", resource.Errorf("deployment declares an A2A endpoint for %s, but it exposes no capabilities", req.AgentID)
+ }
+ version := req.AgentID[strings.LastIndex(req.AgentID, "@")+1:]
+ card := jsonx.Obj{
+ {K: "protocolVersion", V: jsonx.Str("0.3.0")},
+ {K: "name", V: jsonx.Str(bundle.DisplayName)},
+ {K: "description", V: jsonx.Str(bundle.Description)},
+ {K: "version", V: jsonx.Str(version)},
+ {K: "url", V: jsonx.Str(endpoint)},
+ {K: "preferredTransport", V: jsonx.Str("JSONRPC")},
+ {K: "capabilities", V: jsonx.Obj{
+ {K: "streaming", V: jsonx.Bool(false)},
+ {K: "pushNotifications", V: jsonx.Bool(false)},
+ }},
+ {K: "defaultInputModes", V: jsonx.Arr{jsonx.Str("application/json")}},
+ {K: "defaultOutputModes", V: jsonx.Arr{jsonx.Str("application/json")}},
+ {K: "skills", V: skills},
+ }
+ return jsonx.Indented(card) + "\n", nil
+}
+
+func resourceName(id string) string {
+ leaf := id[strings.LastIndex(id, "/")+1:]
+ return strings.SplitN(leaf, "@", 2)[0]
+}
+
+func loadRequirements(root string) ([]requirements, error) {
+ result := []requirements{}
+ err := filepath.WalkDir(root, func(path string, entry fs.DirEntry, walkErr error) error {
+ if walkErr != nil {
+ return walkErr
+ }
+ if entry.IsDir() || entry.Name() != "link.json" || filepath.Base(filepath.Dir(path)) != ".typeference" {
+ return nil
+ }
+ data, err := os.ReadFile(path)
+ if err != nil {
+ return err
+ }
+ var req requirements
+ decoder := json.NewDecoder(bytes.NewReader(data))
+ decoder.DisallowUnknownFields()
+ if err := decoder.Decode(&req); err != nil || req.SchemaVersion != 1 || !resource.IsResourceID(req.AgentID) {
+ return resource.Errorf("Invalid link requirements: %s", path)
+ }
+ agentRoot := filepath.Dir(filepath.Dir(path))
+ req.path = path
+ req.slug = filepath.Base(agentRoot)
+ result = append(result, req)
+ return nil
+ })
+ if err != nil {
+ return nil, resource.Errorf("Cannot inspect built target: %s", err)
+ }
+ sort.Slice(result, func(i, j int) bool { return result[i].AgentID < result[j].AgentID })
+ return result, nil
+}
+
+func validateBindings(req requirements, deployment *File) error {
+ artifact, ok := deployment.Artifacts[req.AgentID]
+ if !ok {
+ return resource.Errorf("deployment does not select artifact %s", req.AgentID)
+ }
+ available := map[string]bool{}
+ for _, mode := range req.Modes {
+ available[mode] = true
+ }
+ selected := map[string]bool{}
+ for _, mode := range artifact.Modes {
+ if !available[mode] {
+ return resource.Errorf("artifact %s does not define selected mode %s", req.AgentID, mode)
+ }
+ selected[mode] = true
+ }
+ if len(req.Modes) > 0 && req.DefaultMode != "" {
+ if len(artifact.Modes) != 1 || artifact.Modes[0] != req.DefaultMode {
+ return resource.Errorf("target %s materializes mode %s for %s; deployment must select exactly that mode",
+ req.Target, req.DefaultMode, req.AgentID)
+ }
+ }
+ if len(req.Modes) > 0 && req.DefaultMode == "" && len(artifact.Modes) == 0 {
+ return resource.Errorf("deployment must select at least one mode for multimodal artifact %s", req.AgentID)
+ }
+ if endpoint := deployment.AgentEndpoints[req.AgentID].A2AURL; endpoint != "" {
+ if req.Target != "neutral" {
+ return resource.Errorf("A2A endpoint for %s requires a neutral artifact, not %s", req.AgentID, req.Target)
+ }
+ if len(req.Modes) > 0 && !selected["a2a"] {
+ return resource.Errorf("A2A endpoint for %s requires the a2a mode to be selected", req.AgentID)
+ }
+ }
+ for _, imported := range req.ToolImports {
+ if imported.Mode != "*" && !selected[imported.Mode] {
+ continue
+ }
+ binding, ok := deployment.ToolBindings[imported.ToolID]
+ if !ok {
+ return resource.Errorf("artifact %s mode %s requires unbound tool %s", req.AgentID, imported.Mode, imported.ToolID)
+ }
+ if _, ok := deployment.Providers[binding.Provider]; !ok {
+ return resource.Errorf("tool %s refers to missing provider %s", imported.ToolID, binding.Provider)
+ }
+ if strings.TrimSpace(binding.RemoteName) == "" {
+ return resource.Errorf("tool %s binding must name the provider's remote tool", imported.ToolID)
+ }
+ }
+ return nil
+}
+
+func codexConfig(req requirements, deployment *File) (string, error) {
+ providerNames := map[string]bool{}
+ artifact := deployment.Artifacts[req.AgentID]
+ selected := map[string]bool{}
+ for _, mode := range artifact.Modes {
+ selected[mode] = true
+ }
+ for _, imported := range req.ToolImports {
+ if imported.Mode != "*" && !selected[imported.Mode] {
+ continue
+ }
+ providerNames[deployment.ToolBindings[imported.ToolID].Provider] = true
+ }
+ names := make([]string, 0, len(providerNames))
+ for name := range providerNames {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ var builder strings.Builder
+ for _, name := range names {
+ provider := deployment.Providers[name]
+ builder.WriteString("[mcp_servers." + tomlKey(name) + "]\n")
+ if provider.Transport == "stdio" {
+ builder.WriteString("command = " + tomlString(provider.Command) + "\n")
+ args := make([]string, len(provider.Args))
+ for i, arg := range provider.Args {
+ args[i] = tomlString(strings.ReplaceAll(arg, "{bundle}", ".typeference/bundle.json"))
+ }
+ builder.WriteString("args = [" + strings.Join(args, ", ") + "]\n")
+ if len(provider.Environment) > 0 {
+ variables := make([]string, 0, len(provider.Environment))
+ for _, reference := range provider.Environment {
+ variables = append(variables, reference.FromEnvironment)
+ }
+ sort.Strings(variables)
+ quoted := make([]string, len(variables))
+ for i, variable := range variables {
+ quoted[i] = tomlString(variable)
+ }
+ builder.WriteString("env_vars = [" + strings.Join(quoted, ", ") + "]\n")
+ }
+ } else {
+ builder.WriteString("url = " + tomlString(provider.URL) + "\n")
+ if provider.BearerTokenEnvironment != "" {
+ builder.WriteString("bearer_token_env_var = " + tomlString(provider.BearerTokenEnvironment) + "\n")
+ }
+ }
+ builder.WriteString("\n")
+ }
+ return builder.String(), nil
+}
+
+func tomlKey(value string) string {
+ safe := true
+ for _, r := range value {
+ if !(r >= 'A' && r <= 'Z') && !(r >= 'a' && r <= 'z') && !(r >= '0' && r <= '9') && r != '_' && r != '-' {
+ safe = false
+ break
+ }
+ }
+ if safe && value != "" {
+ return value
+ }
+ return tomlString(value)
+}
+
+func tomlString(value string) string {
+ var builder strings.Builder
+ builder.WriteByte('"')
+ for _, r := range value {
+ switch r {
+ case '\\':
+ builder.WriteString(`\\`)
+ case '"':
+ builder.WriteString(`\"`)
+ case '\b':
+ builder.WriteString(`\b`)
+ case '\t':
+ builder.WriteString(`\t`)
+ case '\n':
+ builder.WriteString(`\n`)
+ case '\f':
+ builder.WriteString(`\f`)
+ case '\r':
+ builder.WriteString(`\r`)
+ default:
+ if r < 0x20 || r == 0x7f {
+ hexValue := strings.ToUpper(strconv.FormatInt(int64(r), 16))
+ builder.WriteString(`\u`)
+ builder.WriteString(strings.Repeat("0", 4-len(hexValue)))
+ builder.WriteString(hexValue)
+ } else {
+ builder.WriteRune(r)
+ }
+ }
+ }
+ builder.WriteByte('"')
+ return builder.String()
+}
+
+func validateHTTPS(raw, label string) error {
+ parsed, err := url.Parse(raw)
+ if err != nil || parsed.Scheme != "https" || parsed.Host == "" || parsed.User != nil {
+ return resource.Errorf("%s must be an absolute HTTPS URL without embedded credentials", label)
+ }
+ return nil
+}
+
+func copyTree(source, destination string) ([]string, error) {
+ written := []string{}
+ err := filepath.WalkDir(source, func(path string, entry fs.DirEntry, walkErr error) error {
+ if walkErr != nil {
+ return walkErr
+ }
+ rel, err := filepath.Rel(source, path)
+ if err != nil {
+ return err
+ }
+ target := filepath.Join(destination, rel)
+ if entry.IsDir() {
+ return os.MkdirAll(target, 0o755)
+ }
+ data, err := os.ReadFile(path)
+ if err != nil {
+ return err
+ }
+ if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
+ return err
+ }
+ if err := os.WriteFile(target, data, 0o644); err != nil {
+ return err
+ }
+ written = append(written, target)
+ return nil
+ })
+ if err != nil {
+ return nil, resource.Errorf("Cannot copy unlinked target: %s", err)
+ }
+ return written, nil
+}
+
+func write(path, content string) error {
+ if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
+ return resource.Errorf("Cannot create linked artifact directory: %s", filepath.Dir(path))
+ }
+ if err := os.WriteFile(path, []byte(strings.ReplaceAll(content, "\r\n", "\n")), 0o644); err != nil {
+ return resource.Errorf("Cannot write linked artifact: %s", path)
+ }
+ return nil
+}
diff --git a/go/internal/deploy/deploy_test.go b/go/internal/deploy/deploy_test.go
new file mode 100644
index 0000000..eb0aab7
--- /dev/null
+++ b/go/internal/deploy/deploy_test.go
@@ -0,0 +1,125 @@
+package deploy
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/buchk/TypeFerence/go/internal/compile"
+)
+
+func TestCodexConfigurationIsMaterializedOnlyAtLink(t *testing.T) {
+ source := t.TempDir()
+ writeTestFile(t, filepath.Join(source, "cap.yaml"), "schemaVersion: 4\nkind: capability\nid: acme/capabilities/c@1.0.0\nvisibility: exposed\n")
+ writeTestFile(t, filepath.Join(source, "tool.yaml"), "schemaVersion: 4\nkind: tool\nid: acme/tools/runtime@1.0.0\n")
+ writeTestFile(t, filepath.Join(source, "skill.yaml"), "schemaVersion: 4\nkind: skill\nid: acme/skills/s@1.0.0\nbinds: acme/capabilities/c@1.0.0\ninstructions: do it\nrequiresTools: [acme/tools/runtime@1.0.0]\n")
+ writeTestFile(t, filepath.Join(source, "agent.yaml"), "schemaVersion: 4\nkind: agent\nid: acme/agents/a@1.0.0\nskills:\n - ref: acme/skills/s@1.0.0\n")
+ built := t.TempDir()
+ if _, err := compile.Build(source, built, []compile.Target{compile.Codex}, nil); err != nil {
+ t.Fatal(err)
+ }
+ deployment := filepath.Join(t.TempDir(), "deploy.yaml")
+ writeTestFile(t, deployment, "schemaVersion: 1\nenvironment: test\nartifacts:\n acme/agents/a@1.0.0:\n modes: []\nproviders:\n runtime:\n kind: mcp\n transport: stdio\n command: 'server\"name'\n args: ['--bundle', '{bundle}']\n environment:\n ACME_TOKEN:\n fromEnvironment: ACME_TOKEN\ntoolBindings:\n acme/tools/runtime@1.0.0:\n provider: runtime\n remoteName: run\n")
+ linked := filepath.Join(t.TempDir(), "linked")
+ if _, err := Link(filepath.Join(built, "codex"), deployment, linked); err != nil {
+ t.Fatal(err)
+ }
+ config, err := os.ReadFile(filepath.Join(linked, "a", ".codex", "config.toml"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ text := string(config)
+ if !strings.Contains(text, `command = "server\"name"`) || !strings.Contains(text, `env_vars = ["ACME_TOKEN"]`) {
+ t.Fatalf("linked config is not structurally escaped/materialized:\n%s", text)
+ }
+ bindings, err := os.ReadFile(filepath.Join(linked, "a", ".typeference", "tool-bindings.json"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !strings.Contains(string(bindings), `"remoteName": "run"`) {
+ t.Fatalf("tool binding did not preserve the provider-level remote name:\n%s", bindings)
+ }
+ if _, err := os.Stat(filepath.Join(linked, ".typeference", "build.json")); !os.IsNotExist(err) {
+ t.Fatalf("linked output must not retain a stale active build index, got %v", err)
+ }
+ if provenance, err := os.ReadFile(filepath.Join(linked, ".typeference", "link-provenance.json")); err != nil ||
+ !strings.Contains(string(provenance), `"artifacts"`) {
+ t.Fatalf("linked artifact digests were not recorded: %v\n%s", err, provenance)
+ }
+}
+
+func TestA2ACardRequiresNeutralArtifactAndA2AMode(t *testing.T) {
+ source := t.TempDir()
+ writeTestFile(t, filepath.Join(source, "cap.yaml"), "schemaVersion: 4\nkind: capability\nid: acme/capabilities/c@1.0.0\nvisibility: exposed\n")
+ writeTestFile(t, filepath.Join(source, "skill.yaml"), "schemaVersion: 4\nkind: skill\nid: acme/skills/s@1.0.0\nbinds: acme/capabilities/c@1.0.0\nvariants:\n manual:\n instructions: talk\n a2a:\n instructions: call\n")
+ writeTestFile(t, filepath.Join(source, "agent.yaml"), "schemaVersion: 4\nkind: agent\nid: acme/agents/a@1.0.0\nskills:\n - ref: acme/skills/s@1.0.0\n")
+ built := t.TempDir()
+ if _, err := compile.Build(source, built, []compile.Target{compile.Neutral, compile.Codex}, nil); err != nil {
+ t.Fatal(err)
+ }
+ deployment := filepath.Join(t.TempDir(), "deploy.yaml")
+ writeTestFile(t, deployment, "schemaVersion: 1\nenvironment: test\nartifacts:\n acme/agents/a@1.0.0:\n modes: [a2a]\nagentEndpoints:\n acme/agents/a@1.0.0:\n a2aUrl: https://agents.example/a\n")
+ linked := filepath.Join(t.TempDir(), "neutral-linked")
+ if _, err := Link(filepath.Join(built, "neutral"), deployment, linked); err != nil {
+ t.Fatal(err)
+ }
+ card, err := os.ReadFile(filepath.Join(linked, "a", ".typeference", "a2a-agent-card.json"))
+ if err != nil || !strings.Contains(string(card), `"url": "https://agents.example/a"`) {
+ t.Fatalf("A2A card did not preserve the authored endpoint: %v\n%s", err, card)
+ }
+ if _, err := Link(filepath.Join(built, "codex"), deployment, filepath.Join(t.TempDir(), "codex-linked")); err == nil ||
+ !strings.Contains(err.Error(), "materializes mode manual") {
+ t.Fatalf("Codex must reject a2a deployment selection, got %v", err)
+ }
+}
+
+func TestLinkRejectsTamperedUnlinkedArtifact(t *testing.T) {
+ source := t.TempDir()
+ writeTestFile(t, filepath.Join(source, "agent.yaml"), "schemaVersion: 4\nkind: agent\nid: acme/agents/a@1.0.0\n")
+ built := t.TempDir()
+ if _, err := compile.Build(source, built, []compile.Target{compile.Neutral}, nil); err != nil {
+ t.Fatal(err)
+ }
+ writeTestFile(t, filepath.Join(built, "neutral", "a", "AGENTS.md"), "tampered\n")
+ deployment := filepath.Join(t.TempDir(), "deploy.yaml")
+ writeTestFile(t, deployment, "schemaVersion: 1\nenvironment: test\nartifacts:\n acme/agents/a@1.0.0:\n modes: []\n")
+ if _, err := Link(filepath.Join(built, "neutral"), deployment, filepath.Join(t.TempDir(), "linked")); err == nil ||
+ !strings.Contains(err.Error(), "digest mismatch") {
+ t.Fatalf("expected tampering to fail integrity verification, got %v", err)
+ }
+}
+
+func TestLinkRejectsOutputThatContainsItsInput(t *testing.T) {
+ source := t.TempDir()
+ writeTestFile(t, filepath.Join(source, "agent.yaml"), "schemaVersion: 4\nkind: agent\nid: acme/agents/a@1.0.0\n")
+ container := t.TempDir()
+ built := filepath.Join(container, "built")
+ if _, err := compile.Build(source, built, []compile.Target{compile.Neutral}, nil); err != nil {
+ t.Fatal(err)
+ }
+ deployment := filepath.Join(t.TempDir(), "deploy.yaml")
+ writeTestFile(t, deployment, "schemaVersion: 1\nenvironment: test\nartifacts:\n acme/agents/a@1.0.0:\n modes: []\n")
+ if _, err := Link(filepath.Join(built, "neutral"), deployment, built); err == nil ||
+ !strings.Contains(err.Error(), "separate sibling") {
+ t.Fatalf("expected containing output to be rejected before reset, got %v", err)
+ }
+}
+
+func TestInvalidEndpointFailsClosed(t *testing.T) {
+ path := filepath.Join(t.TempDir(), "deploy.yaml")
+ writeTestFile(t, path, "schemaVersion: 1\nenvironment: test\nagentEndpoints:\n acme/agents/a@1.0.0:\n a2aUrl: http://insecure.example/a\n")
+ if _, _, err := Load(path); err == nil {
+ t.Fatal("an insecure A2A endpoint must be rejected")
+ }
+}
+
+func writeTestFile(t *testing.T, path, content string) {
+ t.Helper()
+ if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/go/internal/eval/equivalence.go b/go/internal/eval/equivalence.go
index 617620d..6f48d21 100644
--- a/go/internal/eval/equivalence.go
+++ b/go/internal/eval/equivalence.go
@@ -100,7 +100,7 @@ func Pack(source, scenariosPath, outDir string, opts PackOptions) (int, error) {
if _, err := compile.Build(source, temp, targets, nil); err != nil {
return 0, err
}
- sourceDigest, err := compile.HashDirectory(sourceRoot)
+ sourceDigest, err := compile.HashSource(sourceRoot)
if err != nil {
return 0, err
}
@@ -116,20 +116,9 @@ func Pack(source, scenariosPath, outDir string, opts PackOptions) (int, error) {
if err := copyTree(bundleDir, workspace); err != nil {
return 0, err
}
- contexts := p.agent.ContextFiles
- if p.skill != nil {
- contexts = p.skill.ContextFiles
- }
- materialized := []string{}
- for _, relative := range contexts {
- destination := filepath.Join(workspace, filepath.FromSlash(relative))
- if _, statErr := os.Stat(destination); statErr == nil {
- continue
- }
- if err := copyFile(filepath.Join(sourceRoot, filepath.FromSlash(relative)), destination); err != nil {
- return 0, err
- }
- materialized = append(materialized, relative)
+ compiledContext := []string{}
+ for _, context := range p.agent.ContextObjects {
+ compiledContext = append(compiledContext, context.ID)
}
if err := os.WriteFile(filepath.Join(cellDir, promptFileName), []byte(p.scenario.Task), 0o644); err != nil {
return 0, err
@@ -142,7 +131,7 @@ func Pack(source, scenariosPath, outDir string, opts PackOptions) (int, error) {
{K: "schemaVersion", V: jsonx.Int(1)},
{K: "scenario", V: scenarioJSON(p.scenario)},
{K: "surface", V: jsonx.Str(target.String())},
- {K: "materializedContext", V: stringArrJSON(materialized)},
+ {K: "compiledContext", V: stringArrJSON(compiledContext)},
{K: "workspaceDigest", V: jsonx.Str(digest)},
}
if err := os.WriteFile(filepath.Join(cellDir, cellFileName), []byte(jsonx.Indented(cell)+"\n"), 0o644); err != nil {
diff --git a/go/internal/eval/equivalence_test.go b/go/internal/eval/equivalence_test.go
index e44ab86..c9749ee 100644
--- a/go/internal/eval/equivalence_test.go
+++ b/go/internal/eval/equivalence_test.go
@@ -97,10 +97,11 @@ func TestPackIsDeterministicAndSelfContained(t *testing.T) {
if string(prompt) != "Report status." {
t.Errorf("PROMPT.txt should be the task verbatim, got %q", string(prompt))
}
- // The compiled bundle and the materialized context are both present.
+ // The compiled bundle contains the typed context; no raw source path is
+ // copied beside it.
for _, relative := range []string{
filepath.Join(workspaceDirName, "AGENTS.md"),
- filepath.Join(workspaceDirName, "context", "organization.md"),
+ filepath.Join(workspaceDirName, ".typeference", "bundle.json"),
cellFileName,
} {
if _, statErr := os.Stat(filepath.Join(codex, relative)); statErr != nil {
@@ -111,9 +112,9 @@ func TestPackIsDeterministicAndSelfContained(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- materialized, ok := member(cell, "materializedContext").(jsonx.Arr)
- if !ok || len(materialized) == 0 {
- t.Error("cell.json should record materialized context files")
+ compiled, ok := member(cell, "compiledContext").(jsonx.Arr)
+ if !ok || len(compiled) == 0 {
+ t.Error("cell.json should record compiled typed context ids")
}
digest := jsonMemberString(cell, "workspaceDigest")
actual, err := compile.HashDirectory(filepath.Join(codex, workspaceDirName))
diff --git a/go/internal/eval/payload.go b/go/internal/eval/payload.go
index e306c5d..ce97c4d 100644
--- a/go/internal/eval/payload.go
+++ b/go/internal/eval/payload.go
@@ -1,8 +1,6 @@
package eval
import (
- "os"
- "path/filepath"
"strings"
"github.com/buchk/TypeFerence/go/internal/jsonx"
@@ -21,8 +19,8 @@ const maxTokens = 16000
// BuildExecutorPayload constructs the exact Messages API request body that
// runs the scenario task against the compiled agent definition. The system
// prompt is the neutral-target instruction surface: the agent's rendered
-// instructions, the focused skill's instructions (if any), and the content of
-// the agent's context files read from the source root.
+// instructions, the focused skill's instructions (if any), and the agent's
+// compiled typed context values.
func BuildExecutorPayload(model string, agent *resolve.ResolvedAgent, skill *resolve.ResolvedSkill, sourceRoot, task string) ([]byte, error) {
system, err := renderSystemPrompt(agent, skill, sourceRoot)
if err != nil {
@@ -106,6 +104,7 @@ func BuildJudgePayload(model string, scenario *Scenario, response string) []byte
// renderSystemPrompt assembles the instruction surface the neutral target
// gives a host: agent instructions, the focused skill, and context content.
func renderSystemPrompt(agent *resolve.ResolvedAgent, skill *resolve.ResolvedSkill, sourceRoot string) (string, error) {
+ _ = sourceRoot // retained for API compatibility; typed context is resolved.
var b strings.Builder
b.WriteString("You are the following agent, compiled from a TypeFerence definition. ")
b.WriteString("Operate strictly within these instructions.\n\n")
@@ -129,19 +128,16 @@ func renderSystemPrompt(agent *resolve.ResolvedAgent, skill *resolve.ResolvedSki
b.WriteString(strings.TrimSpace(skill.Instructions))
b.WriteString("\n")
}
- contexts := agent.ContextFiles
- if skill != nil {
- contexts = skill.ContextFiles
- }
- for _, relative := range contexts {
- content, err := os.ReadFile(filepath.Join(sourceRoot, filepath.FromSlash(relative)))
- if err != nil {
- return "", err
- }
+ for _, context := range agent.ContextObjects {
b.WriteString("\n## Context: ")
- b.WriteString(relative)
+ b.WriteString(context.ID)
b.WriteString("\n\n")
- b.WriteString(strings.ReplaceAll(strings.TrimPrefix(string(content), string(rune(0xFEFF))), "\r\n", "\n"))
+ if context.ValuesJSON != "" && context.ValuesJSON != "{}" {
+ b.WriteString("Values: ")
+ b.WriteString(context.ValuesJSON)
+ b.WriteString("\n\n")
+ }
+ b.WriteString(context.Content)
b.WriteString("\n")
}
return b.String(), nil
diff --git a/go/internal/lsp/features.go b/go/internal/lsp/features.go
new file mode 100644
index 0000000..608e9e3
--- /dev/null
+++ b/go/internal/lsp/features.go
@@ -0,0 +1,84 @@
+package lsp
+
+import (
+ "regexp"
+ "strings"
+)
+
+// topLevelFields are the resource fields offered as completions at column 0.
+var topLevelFields = []string{
+ "schemaVersion", "kind", "id", "displayName", "description", "binds",
+ "emit", "embeds", "requiresSlots", "requiresCapabilities", "slots",
+ "workingNorms", "context", "skills", "instructions",
+ "inputSchema", "outputSchema", "contextType", "fields", "body", "values",
+ "requiresContextTypes", "requiresTools", "visibility", "variants",
+ "allowedContextTypes",
+}
+
+// kinds are the resource kinds offered after `kind:`.
+var kinds = []string{"agent", "profile", "interface", "capability", "skill", "context", "contextType", "tool"}
+
+// idToken matches a resource identifier (namespace/name@semver).
+var idToken = regexp.MustCompile(`[a-z0-9][a-z0-9.-]*(?:/[a-z0-9][a-z0-9.-]*)+@[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?`)
+
+// completions returns completion labels for the cursor: kind values after
+// `kind:`, otherwise the top-level field names when the cursor is in a line's
+// leading token (no colon yet).
+func completions(text string, line, char int) []string {
+ cur := lineAt(text, line)
+ prefix := cur
+ if char >= 0 && char <= len(cur) {
+ prefix = cur[:char]
+ }
+ if strings.HasPrefix(strings.TrimSpace(prefix), "kind:") {
+ return kinds
+ }
+ if !strings.Contains(prefix, ":") {
+ return topLevelFields
+ }
+ return nil
+}
+
+func lineAt(text string, line int) string {
+ lines := strings.Split(text, "\n")
+ if line < 0 || line >= len(lines) {
+ return ""
+ }
+ return strings.TrimRight(lines[line], "\r")
+}
+
+// tokenAt returns the resource-id token spanning the cursor, or "".
+func tokenAt(text string, line, char int) string {
+ cur := lineAt(text, line)
+ for _, m := range idToken.FindAllStringIndex(cur, -1) {
+ if char >= m[0] && char <= m[1] {
+ return cur[m[0]:m[1]]
+ }
+ }
+ return ""
+}
+
+// symbolOf extracts (id, kind) from a resource's text for a document symbol.
+func symbolOf(text string) (id, kind string) {
+ for _, raw := range strings.Split(text, "\n") {
+ if v, ok := scalarField(raw, "id"); ok {
+ id = v
+ }
+ if v, ok := scalarField(raw, "kind"); ok {
+ kind = v
+ }
+ }
+ return id, kind
+}
+
+func scalarField(line, name string) (string, bool) {
+ t := strings.TrimSpace(strings.TrimRight(line, "\r"))
+ if strings.HasPrefix(t, name+":") {
+ return strings.TrimSpace(strings.TrimPrefix(t, name+":")), true
+ }
+ return "", false
+}
+
+func isSource(path string) bool {
+ return strings.HasSuffix(path, ".tfer") || strings.HasSuffix(path, ".yaml")
+}
diff --git a/go/internal/lsp/features_test.go b/go/internal/lsp/features_test.go
new file mode 100644
index 0000000..1a2c5b2
--- /dev/null
+++ b/go/internal/lsp/features_test.go
@@ -0,0 +1,136 @@
+package lsp
+
+import (
+ "bytes"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func TestCompletionsKindAndFields(t *testing.T) {
+ if got := completions("kind: ", 0, 6); len(got) == 0 || got[0] != "agent" {
+ t.Errorf("expected kind completions after `kind:`, got %v", got)
+ }
+ fields := completions("sch", 0, 3)
+ if !contains(fields, "schemaVersion") {
+ t.Errorf("expected field completions to include schemaVersion, got %v", fields)
+ }
+ if got := completions("id: foo", 0, 7); got != nil {
+ t.Errorf("expected no completions in a value position, got %v", got)
+ }
+}
+
+func TestTokenAtAndSymbolOf(t *testing.T) {
+ text := "kind: skill\nid: acme/skills/s@1.0.0\nbinds: acme/capabilities/c@1.0.0\n"
+ if tok := tokenAt(text, 2, 15); tok != "acme/capabilities/c@1.0.0" {
+ t.Errorf("tokenAt on the binds id: got %q", tok)
+ }
+ id, kind := symbolOf(text)
+ if id != "acme/skills/s@1.0.0" || kind != "skill" {
+ t.Errorf("symbolOf: got (%q, %q)", id, kind)
+ }
+}
+
+func writeFile(t *testing.T, root, name, content string) string {
+ t.Helper()
+ full := filepath.Join(root, name)
+ if err := os.WriteFile(full, []byte(content), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ return full
+}
+
+func runSession(t *testing.T, rootURI string, msgs ...string) []map[string]any {
+ t.Helper()
+ input := frame("initialize", 1, map[string]any{"rootUri": rootURI})
+ for _, m := range msgs {
+ input += m
+ }
+ input += frame("exit", nil, nil)
+ var out bytes.Buffer
+ if err := NewServer("test").Run(strings.NewReader(input), &out); err != nil {
+ t.Fatal(err)
+ }
+ return readFrames(t, out.String())
+}
+
+func resultFor(frames []map[string]any, id int) any {
+ for _, m := range frames {
+ if n, ok := m["id"].(float64); ok && int(n) == id {
+ return m["result"]
+ }
+ }
+ return nil
+}
+
+func TestDefinitionResolvesResourceID(t *testing.T) {
+ root := t.TempDir()
+ writeFile(t, root, "cap.yaml", "schemaVersion: 4\nkind: capability\nid: acme/cap/c@1.0.0\n")
+ skillText := "schemaVersion: 4\nkind: skill\nid: acme/skills/s@1.0.0\nbinds: acme/cap/c@1.0.0\n"
+ skillURI := pathToURI(writeFile(t, root, "skill.yaml", skillText))
+ frames := runSession(t, pathToURI(root),
+ frame("textDocument/didOpen", nil, docParams(skillURI, skillText)),
+ frame("textDocument/definition", 2, map[string]any{
+ "textDocument": map[string]any{"uri": skillURI},
+ "position": map[string]any{"line": 3, "character": 12}, // on the binds id
+ }),
+ )
+ res, _ := resultFor(frames, 2).(map[string]any)
+ uri, _ := res["uri"].(string)
+ if !strings.Contains(uri, "cap.yaml") {
+ t.Errorf("definition should resolve the binds id to cap.yaml, got %q", uri)
+ }
+}
+
+func TestDocumentSymbolReturnsResource(t *testing.T) {
+ root := t.TempDir()
+ text := "schemaVersion: 4\nkind: capability\nid: acme/cap/c@1.0.0\n"
+ uri := pathToURI(writeFile(t, root, "cap.yaml", text))
+ frames := runSession(t, pathToURI(root),
+ frame("textDocument/didOpen", nil, docParams(uri, text)),
+ frame("textDocument/documentSymbol", 2, map[string]any{
+ "textDocument": map[string]any{"uri": uri},
+ }),
+ )
+ syms, _ := resultFor(frames, 2).([]any)
+ if len(syms) != 1 {
+ t.Fatalf("expected one document symbol, got %v", syms)
+ }
+ name, _ := syms[0].(map[string]any)["name"].(string)
+ if !strings.Contains(name, "acme/cap/c@1.0.0") {
+ t.Errorf("symbol name should include the id, got %q", name)
+ }
+}
+
+func TestCompositionDiagnosticSurfaces(t *testing.T) {
+ root := t.TempDir()
+ // skill binds a capability that does not exist -> workspace does not compose
+ agentText := "schemaVersion: 4\nkind: agent\nid: acme/agent@1.0.0\nskills:\n - ref: acme/skills/s@1.0.0\n"
+ agentURI := pathToURI(writeFile(t, root, "agent.yaml", agentText))
+ writeFile(t, root, "skill.yaml", "schemaVersion: 4\nkind: skill\nid: acme/skills/s@1.0.0\nbinds: acme/cap/missing@1.0.0\n")
+ frames := runSession(t, pathToURI(root),
+ frame("textDocument/didOpen", nil, docParams(agentURI, agentText)),
+ )
+ found := false
+ for _, m := range frames {
+ if m["method"] == "textDocument/publishDiagnostics" {
+ p := m["params"].(map[string]any)
+ if p["uri"] == agentURI && len(p["diagnostics"].([]any)) >= 1 {
+ found = true
+ }
+ }
+ }
+ if !found {
+ t.Error("expected a composition diagnostic for a workspace that does not resolve")
+ }
+}
+
+func contains(s []string, v string) bool {
+ for _, x := range s {
+ if x == v {
+ return true
+ }
+ }
+ return false
+}
diff --git a/go/internal/lsp/server.go b/go/internal/lsp/server.go
new file mode 100644
index 0000000..a124f83
--- /dev/null
+++ b/go/internal/lsp/server.go
@@ -0,0 +1,491 @@
+// Package lsp implements a minimal Language Server Protocol server for
+// TypeFerence sources (`.tfer` and `.yaml`). It provides authoring diagnostics
+// by running the loader's single-document shape validation on each open buffer.
+//
+// Scope: this is the v0 surface. It reports per-file syntax and shape errors —
+// malformed frontmatter fences, unknown fields, bad kinds, skills that do not
+// bind a capability, context objects without a contextType, and so on. It does
+// not yet resolve across a source tree, so composition diagnostics (embedding
+// ambiguity, unsatisfied interfaces, unresolved references) are a planned
+// follow-up that requires whole-workspace resolution and source-root discovery.
+package lsp
+
+import (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/fs"
+ "net/url"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "sync"
+
+ "github.com/buchk/TypeFerence/go/internal/compile"
+ "github.com/buchk/TypeFerence/go/internal/resource"
+)
+
+// Server is a single-connection LSP server speaking JSON-RPC 2.0 over a stream.
+type Server struct {
+ version string
+ r *bufio.Reader
+ w io.Writer
+ wmu sync.Mutex
+ docs map[string]string // uri -> latest buffer text
+ roots []string // workspace root paths
+ index map[string]string // resource id -> defining file uri
+}
+
+// NewServer returns a server that reports the given version to clients.
+func NewServer(version string) *Server {
+ return &Server{version: version, docs: map[string]string{}, index: map[string]string{}}
+}
+
+// message is a JSON-RPC 2.0 request, response, or notification.
+type message struct {
+ JSONRPC string `json:"jsonrpc"`
+ ID json.RawMessage `json:"id,omitempty"`
+ Method string `json:"method,omitempty"`
+ Params json.RawMessage `json:"params,omitempty"`
+ Result json.RawMessage `json:"result,omitempty"`
+ Error *rpcError `json:"error,omitempty"`
+}
+
+type rpcError struct {
+ Code int `json:"code"`
+ Message string `json:"message"`
+}
+
+// Run serves requests from in and writes responses to out until the client
+// sends `exit` or the stream closes.
+func (s *Server) Run(in io.Reader, out io.Writer) error {
+ s.r = bufio.NewReader(in)
+ s.w = out
+ for {
+ msg, err := s.read()
+ if err != nil {
+ if err == io.EOF {
+ return nil
+ }
+ return err
+ }
+ if s.dispatch(msg) {
+ return nil
+ }
+ }
+}
+
+// read parses one Content-Length-framed JSON-RPC message.
+func (s *Server) read() (*message, error) {
+ var length int
+ for {
+ line, err := s.r.ReadString('\n')
+ if err != nil {
+ return nil, err
+ }
+ line = strings.TrimRight(line, "\r\n")
+ if line == "" {
+ break
+ }
+ if strings.HasPrefix(strings.ToLower(line), "content-length:") {
+ fmt.Sscanf(strings.TrimSpace(line[len("content-length:"):]), "%d", &length)
+ }
+ }
+ if length == 0 {
+ return &message{}, nil
+ }
+ buf := make([]byte, length)
+ if _, err := io.ReadFull(s.r, buf); err != nil {
+ return nil, err
+ }
+ var msg message
+ if err := json.Unmarshal(buf, &msg); err != nil {
+ return nil, err
+ }
+ return &msg, nil
+}
+
+func (s *Server) write(msg *message) {
+ msg.JSONRPC = "2.0"
+ body, err := json.Marshal(msg)
+ if err != nil {
+ return
+ }
+ s.wmu.Lock()
+ defer s.wmu.Unlock()
+ fmt.Fprintf(s.w, "Content-Length: %d\r\n\r\n", len(body))
+ s.w.Write(body)
+}
+
+// dispatch handles one message and reports whether the server should exit.
+func (s *Server) dispatch(msg *message) bool {
+ switch msg.Method {
+ case "initialize":
+ s.handleInitialize(msg.Params)
+ s.reply(msg.ID, s.initializeResult())
+ case "initialized":
+ // no-op
+ case "textDocument/didOpen":
+ s.handleOpen(msg.Params)
+ case "textDocument/didChange":
+ s.handleChange(msg.Params)
+ case "textDocument/didSave":
+ s.handleSave(msg.Params)
+ case "textDocument/didClose":
+ s.handleClose(msg.Params)
+ case "textDocument/completion":
+ s.reply(msg.ID, s.handleCompletion(msg.Params))
+ case "textDocument/definition":
+ s.reply(msg.ID, s.handleDefinition(msg.Params))
+ case "textDocument/documentSymbol":
+ s.reply(msg.ID, s.handleDocumentSymbol(msg.Params))
+ case "shutdown":
+ s.reply(msg.ID, nil)
+ case "exit":
+ return true
+ default:
+ if len(msg.ID) > 0 {
+ s.write(&message{ID: msg.ID, Error: &rpcError{Code: -32601, Message: "method not found: " + msg.Method}})
+ }
+ }
+ return false
+}
+
+// handleInitialize captures workspace roots and indexes resource ids.
+func (s *Server) handleInitialize(raw json.RawMessage) {
+ var p struct {
+ RootURI string `json:"rootUri"`
+ WorkspaceFolders []struct {
+ URI string `json:"uri"`
+ } `json:"workspaceFolders"`
+ }
+ json.Unmarshal(raw, &p)
+ seen := map[string]bool{}
+ add := func(uri string) {
+ if uri == "" {
+ return
+ }
+ path := uriToPath(uri)
+ if !seen[path] {
+ seen[path] = true
+ s.roots = append(s.roots, path)
+ }
+ }
+ add(p.RootURI)
+ for _, f := range p.WorkspaceFolders {
+ add(f.URI)
+ }
+ s.buildIndex()
+}
+
+// buildIndex maps every resource id under the workspace roots to its file uri.
+func (s *Server) buildIndex() {
+ index := map[string]string{}
+ for _, root := range s.roots {
+ filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
+ if err != nil || d.IsDir() || !isSource(path) {
+ return nil
+ }
+ raw, readErr := os.ReadFile(path)
+ if readErr != nil {
+ return nil
+ }
+ if id, _ := symbolOf(string(raw)); id != "" {
+ index[id] = pathToURI(path)
+ }
+ return nil
+ })
+ }
+ s.index = index
+}
+
+func (s *Server) reply(id json.RawMessage, result any) {
+ if len(id) == 0 {
+ return
+ }
+ raw := json.RawMessage("null")
+ if result != nil {
+ b, err := json.Marshal(result)
+ if err != nil {
+ return
+ }
+ raw = b
+ }
+ s.write(&message{ID: id, Result: raw})
+}
+
+func (s *Server) initializeResult() any {
+ return map[string]any{
+ "capabilities": map[string]any{
+ // 1 = full document sync: each change carries the whole buffer.
+ "textDocumentSync": 1,
+ "completionProvider": map[string]any{},
+ "definitionProvider": true,
+ "documentSymbolProvider": true,
+ },
+ "serverInfo": map[string]any{
+ "name": "typeference-lsp",
+ "version": s.version,
+ },
+ }
+}
+
+type textDocumentItem struct {
+ URI string `json:"uri"`
+ Text string `json:"text"`
+}
+
+func (s *Server) handleOpen(raw json.RawMessage) {
+ var p struct {
+ TextDocument textDocumentItem `json:"textDocument"`
+ }
+ if json.Unmarshal(raw, &p) != nil {
+ return
+ }
+ s.docs[p.TextDocument.URI] = p.TextDocument.Text
+ s.publish(p.TextDocument.URI, p.TextDocument.Text, true)
+}
+
+func (s *Server) handleChange(raw json.RawMessage) {
+ var p struct {
+ TextDocument struct {
+ URI string `json:"uri"`
+ } `json:"textDocument"`
+ ContentChanges []struct {
+ Text string `json:"text"`
+ } `json:"contentChanges"`
+ }
+ if json.Unmarshal(raw, &p) != nil || len(p.ContentChanges) == 0 {
+ return
+ }
+ text := p.ContentChanges[len(p.ContentChanges)-1].Text
+ s.docs[p.TextDocument.URI] = text
+ // Keystroke: fast single-file shape diagnostics only, no whole-workspace
+ // resolution (it reads from disk and would be noisy mid-edit).
+ s.publish(p.TextDocument.URI, text, false)
+}
+
+func (s *Server) handleSave(raw json.RawMessage) {
+ uri := uriParam(raw)
+ s.buildIndex() // disk changed; refresh id -> uri
+ if text, ok := s.docs[uri]; ok {
+ s.publish(uri, text, true)
+ }
+}
+
+func (s *Server) handleClose(raw json.RawMessage) {
+ uri := uriParam(raw)
+ delete(s.docs, uri)
+ s.publishDiagnostics(uri, nil)
+}
+
+func uriParam(raw json.RawMessage) string {
+ var p struct {
+ TextDocument struct {
+ URI string `json:"uri"`
+ } `json:"textDocument"`
+ }
+ json.Unmarshal(raw, &p)
+ return p.TextDocument.URI
+}
+
+// publish validates one buffer and pushes its diagnostics. When withComposition
+// is set it also reports whole-workspace resolution errors that reference this
+// file (composition diagnostics read from disk, so they run on open/save).
+func (s *Server) publish(uri, text string, withComposition bool) {
+ path := uriToPath(uri)
+ if !isSource(path) {
+ return
+ }
+ var diags []diagnostic
+ if err := resource.CheckDocument(path, text); err != nil {
+ diags = append(diags, diagnostic{
+ Range: errorRange(text),
+ Severity: 1, // Error
+ Source: "typeference",
+ Message: stripFilePrefix(err.Error(), filepath.Base(path)),
+ })
+ } else if withComposition {
+ for _, m := range s.compositionErrorsFor(text, path) {
+ diags = append(diags, diagnostic{
+ Range: errorRange(text),
+ Severity: 1,
+ Source: "typeference",
+ Message: m,
+ })
+ }
+ }
+ s.publishDiagnostics(uri, diags)
+}
+
+// compositionErrorsFor resolves each workspace root from disk and returns the
+// resolution errors to show on this file: those that name this file (by id or
+// basename), plus any that cannot be attributed to a different indexed
+// resource (so an unlocated workspace error still surfaces somewhere).
+func (s *Server) compositionErrorsFor(text, path string) []string {
+ id, _ := symbolOf(text)
+ base := filepath.Base(path)
+ seen := map[string]bool{}
+ out := []string{}
+ for _, root := range s.roots {
+ _, err := compile.Validate(root, "")
+ if err == nil {
+ continue
+ }
+ m := err.Error()
+ if seen[m] {
+ continue
+ }
+ seen[m] = true
+ if strings.Contains(m, base) || (id != "" && strings.Contains(m, id)) || !s.attributableElsewhere(m, id) {
+ out = append(out, m)
+ }
+ }
+ return out
+}
+
+// attributableElsewhere reports whether a message names an indexed resource
+// other than selfID (so it belongs on that resource's file, not this one).
+func (s *Server) attributableElsewhere(msg, selfID string) bool {
+ for otherID := range s.index {
+ if otherID != selfID && strings.Contains(msg, otherID) {
+ return true
+ }
+ }
+ return false
+}
+
+// handleCompletion offers kind values and top-level field names.
+func (s *Server) handleCompletion(raw json.RawMessage) any {
+ uri, line, char := positionParams(raw)
+ items := []map[string]any{}
+ for _, label := range completions(s.docs[uri], line, char) {
+ items = append(items, map[string]any{"label": label})
+ }
+ return map[string]any{"isIncomplete": false, "items": items}
+}
+
+// handleDefinition jumps from a resource-id token to its defining file.
+func (s *Server) handleDefinition(raw json.RawMessage) any {
+ uri, line, char := positionParams(raw)
+ id := tokenAt(s.docs[uri], line, char)
+ target, ok := s.index[id]
+ if id == "" || !ok {
+ return nil
+ }
+ return map[string]any{
+ "uri": target,
+ "range": rng{Start: position{0, 0}, End: position{0, 0}},
+ }
+}
+
+// handleDocumentSymbol returns the resource as a single symbol (id + kind).
+func (s *Server) handleDocumentSymbol(raw json.RawMessage) any {
+ uri := uriParam(raw)
+ id, kind := symbolOf(s.docs[uri])
+ if id == "" {
+ return []any{}
+ }
+ name := id
+ if kind != "" {
+ name = kind + " " + id
+ }
+ // SymbolKind 5 = Class; a resource is the closest analogue.
+ return []map[string]any{{
+ "name": name,
+ "kind": 5,
+ "range": rng{Start: position{0, 0}, End: position{0, 0}},
+ "selectionRange": rng{Start: position{0, 0}, End: position{0, 0}},
+ }}
+}
+
+func positionParams(raw json.RawMessage) (uri string, line, char int) {
+ var p struct {
+ TextDocument struct {
+ URI string `json:"uri"`
+ } `json:"textDocument"`
+ Position struct {
+ Line int `json:"line"`
+ Character int `json:"character"`
+ } `json:"position"`
+ }
+ json.Unmarshal(raw, &p)
+ return p.TextDocument.URI, p.Position.Line, p.Position.Character
+}
+
+func (s *Server) publishDiagnostics(uri string, diags []diagnostic) {
+ if diags == nil {
+ diags = []diagnostic{}
+ }
+ params, err := json.Marshal(map[string]any{
+ "uri": uri,
+ "diagnostics": diags,
+ })
+ if err != nil {
+ return
+ }
+ s.write(&message{Method: "textDocument/publishDiagnostics", Params: params})
+}
+
+type position struct {
+ Line int `json:"line"`
+ Character int `json:"character"`
+}
+
+type rng struct {
+ Start position `json:"start"`
+ End position `json:"end"`
+}
+
+type diagnostic struct {
+ Range rng `json:"range"`
+ Severity int `json:"severity"`
+ Source string `json:"source"`
+ Message string `json:"message"`
+}
+
+// errorRange anchors a whole-document diagnostic to the first line. The loader
+// reports one shape error per document without a position, so v0 highlights the
+// opening line rather than guessing an offset.
+func errorRange(text string) rng {
+ end := 1
+ if nl := strings.IndexByte(text, '\n'); nl > 0 {
+ end = nl
+ }
+ return rng{Start: position{0, 0}, End: position{0, end}}
+}
+
+// stripFilePrefix removes a leading "basename: " that loader errors carry, since
+// the client already associates the diagnostic with the file by URI.
+func stripFilePrefix(msg, base string) string {
+ return strings.TrimPrefix(msg, base+": ")
+}
+
+// pathToURI converts a local filesystem path to a file:// URI, adding the
+// leading slash before a Windows drive letter (C:\x -> file:///C:/x).
+func pathToURI(path string) string {
+ p := filepath.ToSlash(path)
+ if runtime.GOOS == "windows" && len(p) >= 2 && p[1] == ':' {
+ p = "/" + p
+ }
+ // url.URL.String() percent-encodes the path, so spaces and other reserved
+ // characters produce a valid file URI.
+ u := url.URL{Scheme: "file", Path: p}
+ return u.String()
+}
+
+// uriToPath converts a file:// URI to a local filesystem path, handling the
+// leading-slash Windows drive form (file:///C:/x -> C:\x).
+func uriToPath(uri string) string {
+ u, err := url.Parse(uri)
+ if err != nil || u.Scheme != "file" {
+ return uri
+ }
+ p := u.Path
+ if runtime.GOOS == "windows" && len(p) >= 3 && p[0] == '/' && p[2] == ':' {
+ p = p[1:]
+ }
+ return filepath.FromSlash(p)
+}
diff --git a/go/internal/lsp/server_test.go b/go/internal/lsp/server_test.go
new file mode 100644
index 0000000..01df8fa
--- /dev/null
+++ b/go/internal/lsp/server_test.go
@@ -0,0 +1,128 @@
+package lsp
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "strings"
+ "testing"
+)
+
+func frame(method string, id any, params any) string {
+ m := map[string]any{"jsonrpc": "2.0", "method": method}
+ if id != nil {
+ m["id"] = id
+ }
+ if params != nil {
+ m["params"] = params
+ }
+ b, _ := json.Marshal(m)
+ return fmt.Sprintf("Content-Length: %d\r\n\r\n%s", len(b), b)
+}
+
+func readFrames(t *testing.T, out string) []map[string]any {
+ t.Helper()
+ var msgs []map[string]any
+ rest := out
+ for {
+ i := strings.Index(rest, "Content-Length: ")
+ if i < 0 {
+ break
+ }
+ rest = rest[i+len("Content-Length: "):]
+ j := strings.Index(rest, "\r\n\r\n")
+ if j < 0 {
+ break
+ }
+ var n int
+ fmt.Sscanf(rest[:j], "%d", &n)
+ body := rest[j+4 : j+4+n]
+ var m map[string]any
+ if err := json.Unmarshal([]byte(body), &m); err != nil {
+ t.Fatalf("bad frame: %v", err)
+ }
+ msgs = append(msgs, m)
+ rest = rest[j+4+n:]
+ }
+ return msgs
+}
+
+func docParams(uri, text string) map[string]any {
+ return map[string]any{"textDocument": map[string]any{"uri": uri, "text": text}}
+}
+
+func TestServerDiagnostics(t *testing.T) {
+ goodSkill := "---\nschemaVersion: 4\nkind: skill\nid: t/skills/s@1.0.0\nbinds: t/capabilities/c@1.0.0\n---\ndo the thing\n"
+ badSkill := "---\nschemaVersion: 4\nkind: skill\nid: t/skills/s@1.0.0\n---\ndo the thing\n" // missing binds
+
+ input := frame("initialize", 1, map[string]any{}) +
+ frame("textDocument/didOpen", nil, docParams("file:///tmp/bad.tfer", badSkill)) +
+ frame("textDocument/didOpen", nil, docParams("file:///tmp/good.tfer", goodSkill)) +
+ frame("shutdown", 2, nil) +
+ frame("exit", nil, nil)
+
+ var out bytes.Buffer
+ if err := NewServer("test").Run(strings.NewReader(input), &out); err != nil {
+ t.Fatal(err)
+ }
+ frames := readFrames(t, out.String())
+
+ sawInit := false
+ badDiags, goodDiags := -1, -1
+ for _, m := range frames {
+ if m["method"] == "textDocument/publishDiagnostics" {
+ p := m["params"].(map[string]any)
+ uri := p["uri"].(string)
+ diags := p["diagnostics"].([]any)
+ switch {
+ case strings.Contains(uri, "bad.tfer"):
+ badDiags = len(diags)
+ case strings.Contains(uri, "good.tfer"):
+ goodDiags = len(diags)
+ }
+ }
+ if r, ok := m["result"].(map[string]any); ok {
+ if _, has := r["capabilities"]; has {
+ sawInit = true
+ }
+ }
+ }
+ if !sawInit {
+ t.Error("expected an initialize result advertising capabilities")
+ }
+ if badDiags != 1 {
+ t.Errorf("bad.tfer: want 1 diagnostic, got %d", badDiags)
+ }
+ if goodDiags != 0 {
+ t.Errorf("good.tfer: want 0 diagnostics, got %d", goodDiags)
+ }
+}
+
+func TestServerBadFrontmatterFenceDiagnostic(t *testing.T) {
+ // A .tfer with no closing fence must produce exactly one diagnostic.
+ broken := "---\nschemaVersion: 4\nkind: skill\nid: t/skills/s@1.0.0\nbinds: t/capabilities/c@1.0.0\n"
+ input := frame("initialize", 1, map[string]any{}) +
+ frame("textDocument/didOpen", nil, docParams("file:///tmp/broken.tfer", broken)) +
+ frame("exit", nil, nil)
+
+ var out bytes.Buffer
+ if err := NewServer("test").Run(strings.NewReader(input), &out); err != nil {
+ t.Fatal(err)
+ }
+ found := false
+ for _, m := range readFrames(t, out.String()) {
+ if m["method"] == "textDocument/publishDiagnostics" {
+ p := m["params"].(map[string]any)
+ diags := p["diagnostics"].([]any)
+ if len(diags) == 1 {
+ msg := diags[0].(map[string]any)["message"].(string)
+ if strings.Contains(msg, "closing '---' frontmatter fence") {
+ found = true
+ }
+ }
+ }
+ }
+ if !found {
+ t.Error("expected a closing-fence diagnostic for the broken .tfer")
+ }
+}
diff --git a/go/internal/packages/load.go b/go/internal/packages/load.go
new file mode 100644
index 0000000..fb77d0e
--- /dev/null
+++ b/go/internal/packages/load.go
@@ -0,0 +1,118 @@
+package packages
+
+import (
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "github.com/buchk/TypeFerence/go/internal/resource"
+)
+
+// LoadDependencies verifies and loads the committed locked graph from the
+// materialized package directory. It performs no network access.
+func LoadDependencies(source, packagesDir string) (map[string]*resource.Document, []LockedPackage, error) {
+ project, err := resource.LoadProject(source)
+ if err != nil {
+ return nil, nil, err
+ }
+ if project == nil || len(project.Dependencies) == 0 {
+ return map[string]*resource.Document{}, nil, nil
+ }
+ lock, err := LoadLock(source)
+ if err != nil {
+ return nil, nil, err
+ }
+ if lock == nil {
+ return nil, nil, resource.Errorf("dependencies are declared but %s is missing; run typeference restore", LockFile)
+ }
+ if lock.Root != project.Name || lock.RootVersion != project.Version ||
+ !sameDependencies(project.Dependencies, directDependencies(lock.Packages, project.Dependencies)) {
+ return nil, nil, resource.Errorf("%s does not match %s; run typeference restore", LockFile, resource.ProjectManifestFile)
+ }
+ if err := validateLockedGraph(lock, project.Dependencies); err != nil {
+ return nil, nil, err
+ }
+ if packagesDir == "" {
+ packagesDir = filepath.Join(source, "obj", "typeference", "packages")
+ }
+ all := map[string]*resource.Document{}
+ seenPackages := map[string]bool{}
+ for _, item := range lock.Packages {
+ if seenPackages[item.Name] {
+ return nil, nil, resource.Errorf("%s contains duplicate package %s", LockFile, item.Name)
+ }
+ seenPackages[item.Name] = true
+ root := filepath.Join(packagesDir, filepath.FromSlash(item.Name), item.Version,
+ strings.TrimPrefix(item.Digest, "sha256:"), "source")
+ files, err := SourceFiles(root)
+ if err != nil {
+ return nil, nil, resource.Errorf("locked package %s is not materialized; run typeference restore --locked", item.Name)
+ }
+ archive := Archive{
+ SchemaVersion: 1, Name: item.Name, Version: item.Version,
+ Dependencies: item.Dependencies, Exports: item.Exports, Files: files,
+ }
+ if Digest(EncodeArchive(archive)) != item.Digest {
+ return nil, nil, resource.Errorf("materialized package %s does not match locked digest", item.Name)
+ }
+ documents, err := resource.Load(root, "")
+ if err != nil {
+ return nil, nil, err
+ }
+ actualExports := make([]string, 0, len(documents))
+ for id := range documents {
+ actualExports = append(actualExports, id)
+ }
+ sort.Strings(actualExports)
+ if strings.Join(actualExports, "\x00") != strings.Join(item.Exports, "\x00") {
+ return nil, nil, resource.Errorf("materialized package %s exports differ from the lockfile", item.Name)
+ }
+ for id, document := range documents {
+ if _, duplicate := all[id]; duplicate {
+ return nil, nil, resource.Errorf("dependency resource id conflict: %s", id)
+ }
+ all[id] = document
+ }
+ }
+ return all, append([]LockedPackage{}, lock.Packages...), nil
+}
+
+func validateLockedGraph(lock *Lock, roots map[string]string) error {
+ items := map[string]LockedPackage{}
+ for _, item := range lock.Packages {
+ items[item.Name] = item
+ }
+ visited := map[string]bool{}
+ visiting := map[string]bool{}
+ var visit func(string, string) error
+ visit = func(name, version string) error {
+ item, exists := items[name]
+ if !exists || item.Version != version {
+ return resource.Errorf("%s does not contain required package %s@%s", LockFile, name, version)
+ }
+ if visiting[name] {
+ return resource.Errorf("%s contains a dependency cycle at %s", LockFile, name)
+ }
+ if visited[name] {
+ return nil
+ }
+ visiting[name] = true
+ for dependency, requiredVersion := range item.Dependencies {
+ if err := visit(dependency, requiredVersion); err != nil {
+ return err
+ }
+ }
+ delete(visiting, name)
+ visited[name] = true
+ return nil
+ }
+ for name, version := range roots {
+ if err := visit(name, version); err != nil {
+ return err
+ }
+ }
+ if len(visited) != len(items) {
+ return resource.Errorf("%s contains packages unreachable from the project dependencies", LockFile)
+ }
+ return nil
+}
diff --git a/go/internal/packages/model.go b/go/internal/packages/model.go
new file mode 100644
index 0000000..4b72e37
--- /dev/null
+++ b/go/internal/packages/model.go
@@ -0,0 +1,253 @@
+// Package packages implements deterministic TypeFerence source packages and
+// locked, offline dependency materialization.
+package packages
+
+import (
+ "crypto/sha256"
+ "encoding/hex"
+ "encoding/json"
+ "os"
+ "path/filepath"
+ "regexp"
+ "sort"
+ "strings"
+
+ "github.com/buchk/TypeFerence/go/internal/jsonx"
+ "github.com/buchk/TypeFerence/go/internal/resource"
+ "gopkg.in/yaml.v3"
+)
+
+const LockFile = "typeference.lock"
+
+var packageDigest = regexp.MustCompile(`^sha256:[0-9a-f]{64}$`)
+
+type File struct {
+ Path string `json:"path"`
+ Content string `json:"content"`
+}
+
+type Archive struct {
+ SchemaVersion int `json:"schemaVersion"`
+ Name string `json:"name"`
+ Version string `json:"version"`
+ Dependencies map[string]string `json:"dependencies"`
+ Exports []string `json:"exports"`
+ Files []File `json:"files"`
+}
+
+type LockedPackage struct {
+ Name string `json:"name"`
+ Version string `json:"version"`
+ Digest string `json:"digest"`
+ Dependencies map[string]string `json:"dependencies"`
+ Exports []string `json:"exports"`
+}
+
+type Lock struct {
+ SchemaVersion int `json:"schemaVersion"`
+ Root string `json:"root"`
+ RootVersion string `json:"rootVersion"`
+ Packages []LockedPackage `json:"packages"`
+}
+
+func EncodeArchive(archive Archive) []byte {
+ dependencies := jsonx.Obj{}
+ for _, name := range sortedMapKeys(archive.Dependencies) {
+ dependencies = append(dependencies, jsonx.Member{K: name, V: jsonx.Str(archive.Dependencies[name])})
+ }
+ exports := jsonx.Arr{}
+ for _, id := range archive.Exports {
+ exports = append(exports, jsonx.Str(id))
+ }
+ files := jsonx.Arr{}
+ for _, file := range archive.Files {
+ files = append(files, jsonx.Obj{
+ {K: "path", V: jsonx.Str(file.Path)},
+ {K: "content", V: jsonx.Str(file.Content)},
+ })
+ }
+ return []byte(jsonx.Indented(jsonx.Obj{
+ {K: "schemaVersion", V: jsonx.Num("1")},
+ {K: "name", V: jsonx.Str(archive.Name)},
+ {K: "version", V: jsonx.Str(archive.Version)},
+ {K: "dependencies", V: dependencies},
+ {K: "exports", V: exports},
+ {K: "files", V: files},
+ }) + "\n")
+}
+
+func DecodeArchive(data []byte) (*Archive, error) {
+ var archive Archive
+ decoder := json.NewDecoder(strings.NewReader(string(data)))
+ decoder.DisallowUnknownFields()
+ if err := decoder.Decode(&archive); err != nil {
+ return nil, resource.Errorf("invalid TypeFerence package: %s", err)
+ }
+ if archive.SchemaVersion != 1 || !resource.IsPackageName(archive.Name) || !resource.IsSemanticVersion(archive.Version) {
+ return nil, resource.Errorf("invalid TypeFerence package identity")
+ }
+ if err := validateDependencies(archive.Dependencies); err != nil {
+ return nil, err
+ }
+ if err := validateExports(archive.Exports); err != nil {
+ return nil, err
+ }
+ last := ""
+ for _, file := range archive.Files {
+ clean := filepath.ToSlash(filepath.Clean(file.Path))
+ if clean != file.Path || clean == "." || strings.HasPrefix(clean, "../") || filepath.IsAbs(file.Path) {
+ return nil, resource.Errorf("package contains unsafe path: %s", file.Path)
+ }
+ if last != "" && file.Path <= last {
+ return nil, resource.Errorf("package files are not in unique canonical order")
+ }
+ last = file.Path
+ }
+ if err := validateArchiveManifest(&archive); err != nil {
+ return nil, err
+ }
+ if string(EncodeArchive(archive)) != string(data) {
+ return nil, resource.Errorf("package is not canonically serialized")
+ }
+ return &archive, nil
+}
+
+func Digest(data []byte) string {
+ sum := sha256.Sum256(data)
+ return "sha256:" + hex.EncodeToString(sum[:])
+}
+
+func EncodeLock(lock Lock) []byte {
+ packages := jsonx.Arr{}
+ for _, item := range lock.Packages {
+ dependencies := jsonx.Obj{}
+ for _, name := range sortedMapKeys(item.Dependencies) {
+ dependencies = append(dependencies, jsonx.Member{K: name, V: jsonx.Str(item.Dependencies[name])})
+ }
+ exports := jsonx.Arr{}
+ for _, id := range item.Exports {
+ exports = append(exports, jsonx.Str(id))
+ }
+ packages = append(packages, jsonx.Obj{
+ {K: "name", V: jsonx.Str(item.Name)},
+ {K: "version", V: jsonx.Str(item.Version)},
+ {K: "digest", V: jsonx.Str(item.Digest)},
+ {K: "dependencies", V: dependencies},
+ {K: "exports", V: exports},
+ })
+ }
+ return []byte(jsonx.Indented(jsonx.Obj{
+ {K: "schemaVersion", V: jsonx.Num("1")},
+ {K: "root", V: jsonx.Str(lock.Root)},
+ {K: "rootVersion", V: jsonx.Str(lock.RootVersion)},
+ {K: "packages", V: packages},
+ }) + "\n")
+}
+
+func LoadLock(source string) (*Lock, error) {
+ data, err := os.ReadFile(filepath.Join(source, LockFile))
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil, nil
+ }
+ return nil, resource.Errorf("%s: %s", LockFile, err)
+ }
+ var lock Lock
+ decoder := json.NewDecoder(strings.NewReader(string(data)))
+ decoder.DisallowUnknownFields()
+ if err := decoder.Decode(&lock); err != nil {
+ return nil, resource.Errorf("%s: invalid lockfile: %s", LockFile, err)
+ }
+ if lock.SchemaVersion != 1 || string(EncodeLock(lock)) != string(data) {
+ return nil, resource.Errorf("%s: lockfile is not canonically serialized", LockFile)
+ }
+ if !resource.IsPackageName(lock.Root) || !resource.IsSemanticVersion(lock.RootVersion) {
+ return nil, resource.Errorf("%s: invalid root package identity", LockFile)
+ }
+ lastName := ""
+ for _, item := range lock.Packages {
+ if !resource.IsPackageName(item.Name) || !resource.IsSemanticVersion(item.Version) ||
+ !packageDigest.MatchString(item.Digest) {
+ return nil, resource.Errorf("%s: invalid locked package identity for %s", LockFile, item.Name)
+ }
+ if lastName != "" && item.Name <= lastName {
+ return nil, resource.Errorf("%s: packages must be in unique canonical order", LockFile)
+ }
+ lastName = item.Name
+ if err := validateDependencies(item.Dependencies); err != nil {
+ return nil, resource.Errorf("%s: package %s: %s", LockFile, item.Name, err)
+ }
+ if err := validateExports(item.Exports); err != nil {
+ return nil, resource.Errorf("%s: package %s: %s", LockFile, item.Name, err)
+ }
+ }
+ return &lock, nil
+}
+
+func validateDependencies(dependencies map[string]string) error {
+ for name, version := range dependencies {
+ if !resource.IsPackageName(name) {
+ return resource.Errorf("invalid dependency package name: %s", name)
+ }
+ if !resource.IsSemanticVersion(version) {
+ return resource.Errorf("dependency %s must use an exact semantic version", name)
+ }
+ }
+ return nil
+}
+
+func validateExports(exports []string) error {
+ if !sort.StringsAreSorted(exports) {
+ return resource.Errorf("package exports are not in canonical order")
+ }
+ last := ""
+ for _, id := range exports {
+ if !resource.IsResourceID(id) {
+ return resource.Errorf("invalid exported resource id: %s", id)
+ }
+ if id == last {
+ return resource.Errorf("package exports are not unique")
+ }
+ last = id
+ }
+ return nil
+}
+
+func validateArchiveManifest(archive *Archive) error {
+ var manifest *File
+ for i := range archive.Files {
+ if archive.Files[i].Path == resource.ProjectManifestFile {
+ manifest = &archive.Files[i]
+ break
+ }
+ }
+ if manifest == nil {
+ return resource.Errorf("package is missing %s", resource.ProjectManifestFile)
+ }
+ var project struct {
+ SchemaVersion int `yaml:"schemaVersion"`
+ Name string `yaml:"name"`
+ Version string `yaml:"version"`
+ Publisher string `yaml:"publisher"`
+ Dependencies map[string]string `yaml:"dependencies"`
+ }
+ decoder := yaml.NewDecoder(strings.NewReader(manifest.Content))
+ decoder.KnownFields(true)
+ if err := decoder.Decode(&project); err != nil || project.SchemaVersion != 2 {
+ return resource.Errorf("package contains an invalid %s", resource.ProjectManifestFile)
+ }
+ if project.Name != archive.Name || project.Version != archive.Version ||
+ !sameDependencies(project.Dependencies, archive.Dependencies) {
+ return resource.Errorf("package envelope does not match %s", resource.ProjectManifestFile)
+ }
+ return nil
+}
+
+func sortedMapKeys(values map[string]string) []string {
+ keys := make([]string, 0, len(values))
+ for key := range values {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+ return keys
+}
diff --git a/go/internal/packages/pack.go b/go/internal/packages/pack.go
new file mode 100644
index 0000000..973e0ec
--- /dev/null
+++ b/go/internal/packages/pack.go
@@ -0,0 +1,111 @@
+package packages
+
+import (
+ "io/fs"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "github.com/buchk/TypeFerence/go/internal/resource"
+)
+
+var excludedSourceDirs = map[string]bool{
+ ".git": true, "dist": true, "bin": true, "obj": true,
+}
+
+// SourceFiles returns the explicit version 4 source resource set. It is shared
+// by pack and source digesting so output/cache directories cannot contaminate
+// identity.
+func SourceFiles(source string) ([]File, error) {
+ root, err := filepath.Abs(source)
+ if err != nil {
+ return nil, resource.Errorf("Source directory not found: %s", source)
+ }
+ files := []File{}
+ err = filepath.WalkDir(root, func(path string, entry fs.DirEntry, walkErr error) error {
+ if walkErr != nil {
+ return walkErr
+ }
+ if entry.IsDir() {
+ if filepath.Dir(path) == root && excludedSourceDirs[entry.Name()] {
+ return filepath.SkipDir
+ }
+ return nil
+ }
+ rel, relErr := filepath.Rel(root, path)
+ if relErr != nil {
+ return relErr
+ }
+ rel = filepath.ToSlash(rel)
+ name := entry.Name()
+ if name != resource.ProjectManifestFile && name != LockFile && name != "typeference.trust.yaml" &&
+ !strings.HasSuffix(name, ".yaml") && !strings.HasSuffix(name, ".tfer") {
+ return nil
+ }
+ data, readErr := os.ReadFile(path)
+ if readErr != nil {
+ return readErr
+ }
+ content := strings.TrimPrefix(strings.ReplaceAll(string(data), "\r\n", "\n"), "\uFEFF")
+ files = append(files, File{Path: rel, Content: content})
+ return nil
+ })
+ if err != nil {
+ return nil, resource.Errorf("Cannot enumerate source package: %s", err)
+ }
+ sort.Slice(files, func(i, j int) bool { return files[i].Path < files[j].Path })
+ return files, nil
+}
+
+func Pack(source, output string) (string, error) {
+ project, err := resource.LoadProject(source)
+ if err != nil {
+ return "", err
+ }
+ if project == nil {
+ return "", resource.Errorf("typeference pack requires %s", resource.ProjectManifestFile)
+ }
+ if len(project.Dependencies) > 0 {
+ lock, err := LoadLock(source)
+ if err != nil {
+ return "", err
+ }
+ if lock == nil || lock.Root != project.Name || lock.RootVersion != project.Version ||
+ !sameDependencies(project.Dependencies, directDependencies(lock.Packages, project.Dependencies)) {
+ return "", resource.Errorf("typeference pack requires a lockfile matching declared dependencies; run typeference restore")
+ }
+ if err := validateLockedGraph(lock, project.Dependencies); err != nil {
+ return "", err
+ }
+ }
+ documents, err := resource.Load(source, "")
+ if err != nil {
+ return "", err
+ }
+ exports := make([]string, 0, len(documents))
+ for id := range documents {
+ exports = append(exports, id)
+ }
+ sort.Strings(exports)
+ files, err := SourceFiles(source)
+ if err != nil {
+ return "", err
+ }
+ archive := Archive{
+ SchemaVersion: 1,
+ Name: project.Name, Version: project.Version,
+ Dependencies: project.Dependencies, Exports: exports, Files: files,
+ }
+ data := EncodeArchive(archive)
+ if output == "" {
+ output = filepath.Join(source, project.Name[strings.LastIndex(project.Name, "/")+1:]+"-"+project.Version+".tferpkg")
+ }
+ if err := os.MkdirAll(filepath.Dir(output), 0o755); err != nil {
+ return "", resource.Errorf("Cannot create package directory: %s", filepath.Dir(output))
+ }
+ if err := os.WriteFile(output, data, 0o644); err != nil {
+ return "", resource.Errorf("Cannot write package: %s", output)
+ }
+ return Digest(data), nil
+}
diff --git a/go/internal/packages/packages_test.go b/go/internal/packages/packages_test.go
new file mode 100644
index 0000000..3d94c77
--- /dev/null
+++ b/go/internal/packages/packages_test.go
@@ -0,0 +1,99 @@
+package packages_test
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/buchk/TypeFerence/go/internal/compile"
+ "github.com/buchk/TypeFerence/go/internal/packages"
+)
+
+func TestRestoreMaterializesTransitiveGraphForOfflineBuild(t *testing.T) {
+ feed := t.TempDir()
+ base := t.TempDir()
+ write(t, base, "typeference.yaml", "schemaVersion: 2\nname: marathon/base\nversion: 1.0.0\n")
+ write(t, base, "profile.yaml", "schemaVersion: 4\nkind: profile\nid: marathon/profiles/base@1.0.0\nworkingNorms: [preserve evidence]\n")
+ packToFeed(t, base, feed, "marathon/base", "1.0.0", "base")
+
+ foundations := t.TempDir()
+ write(t, foundations, "typeference.yaml", "schemaVersion: 2\nname: marathon/foundations\nversion: 2.0.0\ndependencies:\n marathon/base: 1.0.0\n")
+ write(t, foundations, "profile.yaml", "schemaVersion: 4\nkind: profile\nid: marathon/profiles/foundations@2.0.0\nembeds: [marathon/profiles/base@1.0.0]\n")
+ restoreProject(t, foundations, feed)
+ packToFeed(t, foundations, feed, "marathon/foundations", "2.0.0", "foundations")
+
+ root := t.TempDir()
+ write(t, root, "typeference.yaml", "schemaVersion: 2\nname: marathon/agents\nversion: 1.0.0\ndependencies:\n marathon/foundations: 2.0.0\n")
+ write(t, root, "agent.yaml", "schemaVersion: 4\nkind: agent\nid: marathon/agents/payments@1.0.0\nembeds: [marathon/profiles/foundations@2.0.0]\n")
+ packagesDir := filepath.Join(root, "obj", "typeference", "packages")
+ restorer := packages.Restorer{
+ Source: root, PackagesDir: packagesDir,
+ Config: &packages.FeedConfig{SchemaVersion: 1, Routes: map[string]packages.Route{
+ "marathon": {Kind: "filesystem", Path: feed},
+ }},
+ }
+ lock, err := restorer.Restore()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(lock.Packages) != 2 {
+ t.Fatalf("expected complete transitive graph, got %+v", lock.Packages)
+ }
+ if _, err := compile.BuildWithOptions(root, t.TempDir(), []compile.Target{compile.Neutral}, nil,
+ compile.BuildOptions{PackagesDir: packagesDir}); err != nil {
+ t.Fatalf("offline build could not consume restored graph: %v", err)
+ }
+}
+
+func restoreProject(t *testing.T, source, feed string) {
+ t.Helper()
+ restorer := packages.Restorer{
+ Source: source,
+ Config: &packages.FeedConfig{SchemaVersion: 1, Routes: map[string]packages.Route{
+ "marathon": {Kind: "filesystem", Path: feed},
+ }},
+ }
+ if _, err := restorer.Restore(); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestPackIsByteDeterministic(t *testing.T) {
+ source := t.TempDir()
+ write(t, source, "typeference.yaml", "schemaVersion: 2\nname: acme/package\nversion: 1.0.0\n")
+ write(t, source, "agent.yaml", "schemaVersion: 4\nkind: agent\nid: acme/agents/a@1.0.0\n")
+ first := filepath.Join(t.TempDir(), "a.tferpkg")
+ second := filepath.Join(t.TempDir(), "b.tferpkg")
+ digestA, err := packages.Pack(source, first)
+ if err != nil {
+ t.Fatal(err)
+ }
+ digestB, err := packages.Pack(source, second)
+ if err != nil {
+ t.Fatal(err)
+ }
+ a, _ := os.ReadFile(first)
+ b, _ := os.ReadFile(second)
+ if digestA != digestB || string(a) != string(b) {
+ t.Fatal("repeated packs were not byte-identical")
+ }
+}
+
+func packToFeed(t *testing.T, source, feed, name, version, leaf string) {
+ t.Helper()
+ output := filepath.Join(feed, filepath.FromSlash(name), version, leaf+"-"+version+".tferpkg")
+ if _, err := packages.Pack(source, output); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func write(t *testing.T, root, name, content string) {
+ t.Helper()
+ path := filepath.Join(root, filepath.FromSlash(name))
+ if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/go/internal/packages/restore.go b/go/internal/packages/restore.go
new file mode 100644
index 0000000..b6457db
--- /dev/null
+++ b/go/internal/packages/restore.go
@@ -0,0 +1,377 @@
+package packages
+
+import (
+ "bytes"
+ "io"
+ "net/http"
+ "net/url"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "github.com/buchk/TypeFerence/go/internal/jsonx"
+ "github.com/buchk/TypeFerence/go/internal/resource"
+ "gopkg.in/yaml.v3"
+)
+
+type Route struct {
+ Kind string `yaml:"kind"`
+ Path string `yaml:"path"`
+ BaseURL string `yaml:"baseUrl"`
+ CredentialEnvironment string `yaml:"credentialEnvironment"`
+ Organization string `yaml:"organization"`
+ Project string `yaml:"project"`
+ Feed string `yaml:"feed"`
+}
+
+type FeedConfig struct {
+ SchemaVersion int `yaml:"schemaVersion"`
+ Routes map[string]Route `yaml:"routes"`
+}
+
+type Restorer struct {
+ Source string
+ PackagesDir string
+ Config *FeedConfig
+ Locked bool
+ client *http.Client
+}
+
+func LoadFeedConfig(path string) (*FeedConfig, error) {
+ if strings.TrimSpace(path) == "" {
+ return nil, resource.Errorf("restore requires --feeds ")
+ }
+ data, err := os.ReadFile(path)
+ if err != nil {
+ return nil, resource.Errorf("Cannot read feed configuration: %s", path)
+ }
+ var config FeedConfig
+ decoder := yaml.NewDecoder(bytes.NewReader(data))
+ decoder.KnownFields(true)
+ if err := decoder.Decode(&config); err != nil {
+ return nil, resource.Errorf("Invalid feed configuration: %s", err)
+ }
+ if config.SchemaVersion != 1 || len(config.Routes) == 0 {
+ return nil, resource.Errorf("feed configuration must use schemaVersion 1 and declare routes")
+ }
+ for prefix, route := range config.Routes {
+ if !resource.IsPackageName(prefix + "/placeholder") {
+ return nil, resource.Errorf("invalid feed route prefix: %s", prefix)
+ }
+ switch route.Kind {
+ case "filesystem":
+ if route.Path == "" || route.BaseURL != "" || route.CredentialEnvironment != "" ||
+ route.Organization != "" || route.Project != "" || route.Feed != "" {
+ return nil, resource.Errorf("filesystem route %s requires path", prefix)
+ }
+ case "http", "jfrog":
+ parsed, err := url.Parse(route.BaseURL)
+ if err != nil || parsed.Scheme != "https" || parsed.Host == "" || parsed.User != nil ||
+ route.Path != "" || route.Organization != "" || route.Project != "" || route.Feed != "" {
+ return nil, resource.Errorf("%s route %s requires an absolute HTTPS baseUrl", route.Kind, prefix)
+ }
+ case "azureArtifacts":
+ parsed, err := url.Parse(route.Organization)
+ if err != nil || parsed.Scheme != "https" || parsed.Host == "" || parsed.User != nil || route.Feed == "" ||
+ route.Path != "" || route.BaseURL != "" || route.CredentialEnvironment != "" {
+ return nil, resource.Errorf("azureArtifacts route %s requires HTTPS organization and feed", prefix)
+ }
+ default:
+ return nil, resource.Errorf("unsupported feed route kind %q", route.Kind)
+ }
+ }
+ return &config, nil
+}
+
+func (r *Restorer) Restore() (*Lock, error) {
+ project, err := resource.LoadProject(r.Source)
+ if err != nil {
+ return nil, err
+ }
+ if project == nil {
+ return nil, resource.Errorf("restore requires %s", resource.ProjectManifestFile)
+ }
+ if r.PackagesDir == "" {
+ r.PackagesDir = filepath.Join(r.Source, "obj", "typeference", "packages")
+ }
+ if r.client == nil {
+ r.client = http.DefaultClient
+ }
+ var existing *Lock
+ if r.Locked {
+ existing, err = LoadLock(r.Source)
+ if err != nil {
+ return nil, err
+ }
+ if existing == nil {
+ return nil, resource.Errorf("--locked requires %s", LockFile)
+ }
+ if existing.Root != project.Name || existing.RootVersion != project.Version {
+ return nil, resource.Errorf("%s root identity does not match %s", LockFile, resource.ProjectManifestFile)
+ }
+ if !sameDependencies(project.Dependencies, directDependencies(existing.Packages, project.Dependencies)) {
+ return nil, resource.Errorf("%s does not match manifest dependencies", LockFile)
+ }
+ if err := validateLockedGraph(existing, project.Dependencies); err != nil {
+ return nil, err
+ }
+ }
+
+ resolved := map[string]LockedPackage{}
+ visiting := map[string]bool{}
+ var visit func(string, string) error
+ visit = func(name, version string) error {
+ if prior, exists := resolved[name]; exists {
+ if prior.Version != version {
+ return resource.Errorf("dependency conflict: %s requested as both %s and %s", name, prior.Version, version)
+ }
+ return nil
+ }
+ if visiting[name] {
+ return resource.Errorf("dependency cycle detected at %s", name)
+ }
+ visiting[name] = true
+ defer delete(visiting, name)
+
+ data, err := r.fetch(name, version)
+ if err != nil {
+ return err
+ }
+ archive, err := DecodeArchive(data)
+ if err != nil {
+ return resource.Errorf("%s@%s: %s", name, version, err)
+ }
+ if archive.Name != name || archive.Version != version {
+ return resource.Errorf("feed returned %s@%s while resolving %s@%s", archive.Name, archive.Version, name, version)
+ }
+ digest := Digest(data)
+ if r.Locked {
+ expected := findLocked(existing.Packages, name)
+ if expected == nil || expected.Version != version || expected.Digest != digest {
+ return resource.Errorf("%s@%s does not match its locked digest", name, version)
+ }
+ }
+ for _, dependency := range sortedMapKeys(archive.Dependencies) {
+ if err := visit(dependency, archive.Dependencies[dependency]); err != nil {
+ return err
+ }
+ }
+ item := LockedPackage{
+ Name: name, Version: version, Digest: digest,
+ Dependencies: archive.Dependencies, Exports: append([]string{}, archive.Exports...),
+ }
+ resolved[name] = item
+ return r.materialize(archive, digest)
+ }
+ for _, name := range project.SortedDependencies() {
+ if err := visit(name, project.Dependencies[name]); err != nil {
+ return nil, err
+ }
+ }
+ names := make([]string, 0, len(resolved))
+ for name := range resolved {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ lock := &Lock{SchemaVersion: 1, Root: project.Name, RootVersion: project.Version}
+ for _, name := range names {
+ lock.Packages = append(lock.Packages, resolved[name])
+ }
+ if r.Locked {
+ if string(EncodeLock(*lock)) != string(EncodeLock(*existing)) {
+ return nil, resource.Errorf("restored graph differs from %s", LockFile)
+ }
+ if err := r.writeResolution(lock); err != nil {
+ return nil, err
+ }
+ return existing, nil
+ }
+ if err := os.WriteFile(filepath.Join(r.Source, LockFile), EncodeLock(*lock), 0o644); err != nil {
+ return nil, resource.Errorf("Cannot write %s", LockFile)
+ }
+ if err := r.writeResolution(lock); err != nil {
+ return nil, err
+ }
+ return lock, nil
+}
+
+func (r *Restorer) writeResolution(lock *Lock) error {
+ items := jsonx.Arr{}
+ for _, item := range lock.Packages {
+ sourcePath := filepath.ToSlash(filepath.Join(filepath.FromSlash(item.Name), item.Version,
+ strings.TrimPrefix(item.Digest, "sha256:"), "source"))
+ items = append(items, jsonx.Obj{
+ {K: "name", V: jsonx.Str(item.Name)},
+ {K: "version", V: jsonx.Str(item.Version)},
+ {K: "digest", V: jsonx.Str(item.Digest)},
+ {K: "source", V: jsonx.Str(sourcePath)},
+ })
+ }
+ content := jsonx.Indented(jsonx.Obj{
+ {K: "schemaVersion", V: jsonx.Num("1")},
+ {K: "packages", V: items},
+ }) + "\n"
+ path := filepath.Join(r.PackagesDir, "resolution.json")
+ if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
+ return resource.Errorf("Cannot create package resolution directory")
+ }
+ if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
+ return resource.Errorf("Cannot write package resolution manifest")
+ }
+ return nil
+}
+
+func (r *Restorer) routeFor(name string) (Route, error) {
+ best := ""
+ var selected Route
+ for prefix, route := range r.Config.Routes {
+ if (name == prefix || strings.HasPrefix(name, prefix+"/")) && len(prefix) > len(best) {
+ best, selected = prefix, route
+ }
+ }
+ if best == "" {
+ return Route{}, resource.Errorf("no feed route for package %s", name)
+ }
+ return selected, nil
+}
+
+func (r *Restorer) fetch(name, version string) ([]byte, error) {
+ route, err := r.routeFor(name)
+ if err != nil {
+ return nil, err
+ }
+ leaf := name[strings.LastIndex(name, "/")+1:] + "-" + version + ".tferpkg"
+ switch route.Kind {
+ case "filesystem":
+ return os.ReadFile(filepath.Join(route.Path, filepath.FromSlash(name), version, leaf))
+ case "http", "jfrog":
+ address := strings.TrimRight(route.BaseURL, "/") + "/" + name + "/" + version + "/" + leaf
+ request, _ := http.NewRequest(http.MethodGet, address, nil)
+ if route.CredentialEnvironment != "" {
+ token := os.Getenv(route.CredentialEnvironment)
+ if token == "" {
+ return nil, resource.Errorf("credential environment variable %s is not set", route.CredentialEnvironment)
+ }
+ request.Header.Set("Authorization", "Bearer "+token)
+ }
+ response, err := r.client.Do(request)
+ if err != nil {
+ return nil, resource.Errorf("cannot restore %s@%s: %s", name, version, err)
+ }
+ defer response.Body.Close()
+ if response.StatusCode >= 300 {
+ return nil, resource.Errorf("cannot restore %s@%s: feed returned %s", name, version, response.Status)
+ }
+ return io.ReadAll(response.Body)
+ case "azureArtifacts":
+ return fetchAzureUniversal(route, name, version)
+ default:
+ return nil, resource.Errorf("unsupported feed kind: %s", route.Kind)
+ }
+}
+
+func fetchAzureUniversal(route Route, name, version string) ([]byte, error) {
+ temp, err := os.MkdirTemp("", "typeference-azure-")
+ if err != nil {
+ return nil, resource.Errorf("cannot create Azure Artifacts staging directory")
+ }
+ defer os.RemoveAll(temp)
+ packageName := strings.ReplaceAll(name, "/", "--")
+ args := []string{"artifacts", "universal", "download", "--organization", route.Organization,
+ "--feed", route.Feed, "--name", packageName, "--version", version, "--path", temp}
+ if route.Project != "" {
+ args = append(args, "--project", route.Project, "--scope", "project")
+ }
+ command := exec.Command("az", args...)
+ if output, err := command.CombinedOutput(); err != nil {
+ return nil, resource.Errorf("Azure Artifacts download failed: %s", strings.TrimSpace(string(output)))
+ }
+ var packagePath string
+ _ = filepath.WalkDir(temp, func(path string, entry os.DirEntry, err error) error {
+ if err == nil && !entry.IsDir() && strings.HasSuffix(entry.Name(), ".tferpkg") {
+ if packagePath == "" {
+ packagePath = path
+ } else {
+ packagePath = "\x00"
+ }
+ }
+ return nil
+ })
+ if packagePath == "" || packagePath == "\x00" {
+ return nil, resource.Errorf("Azure Universal Package must contain exactly one .tferpkg file")
+ }
+ return os.ReadFile(packagePath)
+}
+
+func (r *Restorer) materialize(archive *Archive, digest string) error {
+ hexDigest := strings.TrimPrefix(digest, "sha256:")
+ root := filepath.Join(r.PackagesDir, filepath.FromSlash(archive.Name), archive.Version, hexDigest, "source")
+ packagesRootAbs, err := filepath.Abs(r.PackagesDir)
+ if err != nil {
+ return resource.Errorf("invalid package materialization directory")
+ }
+ rootAbs, err := filepath.Abs(root)
+ if err != nil {
+ return resource.Errorf("invalid package materialization path")
+ }
+ relative, err := filepath.Rel(packagesRootAbs, rootAbs)
+ if err != nil || relative == "." || strings.HasPrefix(relative, ".."+string(filepath.Separator)) || filepath.IsAbs(relative) {
+ return resource.Errorf("package materialization path escapes package directory")
+ }
+ // The digest-qualified directory is derived output. Recreate it exactly so
+ // an interrupted prior restore cannot leave undeclared source files behind.
+ if err := os.RemoveAll(rootAbs); err != nil {
+ return resource.Errorf("cannot reset package materialization directory")
+ }
+ if err := os.MkdirAll(root, 0o755); err != nil {
+ return resource.Errorf("cannot create package materialization directory")
+ }
+ for _, file := range archive.Files {
+ target := filepath.Join(root, filepath.FromSlash(file.Path))
+ full, _ := filepath.Abs(target)
+ rootAbs, _ := filepath.Abs(root)
+ if !strings.HasPrefix(full, rootAbs+string(filepath.Separator)) {
+ return resource.Errorf("package path escapes materialization root: %s", file.Path)
+ }
+ if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
+ return resource.Errorf("cannot materialize package path: %s", file.Path)
+ }
+ if err := os.WriteFile(target, []byte(file.Content), 0o644); err != nil {
+ return resource.Errorf("cannot materialize package file: %s", file.Path)
+ }
+ }
+ return nil
+}
+
+func findLocked(items []LockedPackage, name string) *LockedPackage {
+ for i := range items {
+ if items[i].Name == name {
+ return &items[i]
+ }
+ }
+ return nil
+}
+
+func directDependencies(items []LockedPackage, declared map[string]string) map[string]string {
+ result := map[string]string{}
+ for name := range declared {
+ if item := findLocked(items, name); item != nil {
+ result[name] = item.Version
+ }
+ }
+ return result
+}
+
+func sameDependencies(a, b map[string]string) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for key, value := range a {
+ if b[key] != value {
+ return false
+ }
+ }
+ return true
+}
diff --git a/go/internal/resolve/exposure_test.go b/go/internal/resolve/exposure_test.go
new file mode 100644
index 0000000..adcd917
--- /dev/null
+++ b/go/internal/resolve/exposure_test.go
@@ -0,0 +1,52 @@
+package resolve
+
+import (
+ "testing"
+
+ "github.com/buchk/TypeFerence/go/internal/resource"
+)
+
+func TestCapabilityInternalByDefault(t *testing.T) {
+ set := baseSkillAgent(nil, nil) // capability t/cap/c@1.0.0 has no visibility
+ agent, err := New(set).Resolve("t/agent@1.0.0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(agent.ExposedSkills()) != 0 {
+ t.Errorf("capabilities are internal by default; expected no exposed skills")
+ }
+ if agent.Skills[0].Exposed {
+ t.Errorf("skill should not be exposed by default")
+ }
+}
+
+func TestExposedCapabilitySurfaces(t *testing.T) {
+ set := baseSkillAgent(nil, nil)
+ set["t/cap/c@1.0.0"].Visibility = "exposed"
+ agent, err := New(set).Resolve("t/agent@1.0.0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ exposed := agent.ExposedSkills()
+ if len(exposed) != 1 || exposed[0].CapabilityID != "t/cap/c@1.0.0" {
+ t.Fatalf("expected the exposed capability on the public surface, got %+v", exposed)
+ }
+}
+
+func TestExposurePromotesThroughEmbedding(t *testing.T) {
+ cap := doc("capability", "t/cap/c@1.0.0", func(d *resource.Document) { d.Visibility = "exposed" })
+ skill := doc("skill", "t/skills/s@1.0.0", func(d *resource.Document) { d.Binds = "t/cap/c@1.0.0" })
+ profile := doc("profile", "t/profile@1.0.0", func(d *resource.Document) {
+ d.Skills = []resource.SkillBinding{{Ref: "t/skills/s@1.0.0"}}
+ })
+ agent := doc("agent", "t/agent@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/profile@1.0.0"}
+ })
+ resolved, err := New(docSet(cap, skill, profile, agent)).Resolve("t/agent@1.0.0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(resolved.ExposedSkills()) != 1 {
+ t.Errorf("exposure should promote through embedding onto the agent's surface")
+ }
+}
diff --git a/go/internal/resolve/gating_test.go b/go/internal/resolve/gating_test.go
new file mode 100644
index 0000000..9f8fb8a
--- /dev/null
+++ b/go/internal/resolve/gating_test.go
@@ -0,0 +1,89 @@
+package resolve
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/buchk/TypeFerence/go/internal/resource"
+)
+
+func TestAllowedContextTypesPermitsRefinement(t *testing.T) {
+ set := baseSkillAgent(nil, func(a *resource.Document) {
+ a.Context = []string{"t/notes/n@1.0.0"}
+ a.AllowedContextTypes = []string{"t/ct/cast@1.0.0"}
+ })
+ set["t/ct/cast@1.0.0"] = doc("contextType", "t/ct/cast@1.0.0", nil)
+ set["t/ct/gov@1.0.0"] = doc("contextType", "t/ct/gov@1.0.0", func(d *resource.Document) { d.Embeds = []string{"t/ct/cast@1.0.0"} })
+ set["t/notes/n@1.0.0"] = doc("context", "t/notes/n@1.0.0", func(d *resource.Document) { d.ContextType = "t/ct/gov@1.0.0" })
+ if _, err := New(set).Resolve("t/agent@1.0.0"); err != nil {
+ t.Fatalf("a governed cast should satisfy an allow-list of [cast]: %v", err)
+ }
+}
+
+func TestAllowedContextTypesRejectsOutsider(t *testing.T) {
+ set := baseSkillAgent(nil, func(a *resource.Document) {
+ a.Context = []string{"t/notes/n@1.0.0"}
+ a.AllowedContextTypes = []string{"t/ct/other@1.0.0"}
+ })
+ set["t/ct/other@1.0.0"] = doc("contextType", "t/ct/other@1.0.0", nil)
+ set["t/ct/cast@1.0.0"] = doc("contextType", "t/ct/cast@1.0.0", nil)
+ set["t/notes/n@1.0.0"] = doc("context", "t/notes/n@1.0.0", func(d *resource.Document) { d.ContextType = "t/ct/cast@1.0.0" })
+ _, err := New(set).Resolve("t/agent@1.0.0")
+ if err == nil || !strings.Contains(err.Error(), "not among the allowed") {
+ t.Fatalf("expected allow-list rejection, got %v", err)
+ }
+}
+
+func TestAllowListIntersectsThroughEmbeds(t *testing.T) {
+ cap := doc("capability", "t/cap/c@1.0.0", nil)
+ skill := doc("skill", "t/skills/s@1.0.0", func(d *resource.Document) { d.Binds = "t/cap/c@1.0.0" })
+ profile := doc("profile", "t/profile@1.0.0", func(d *resource.Document) {
+ d.Skills = []resource.SkillBinding{{Ref: "t/skills/s@1.0.0"}}
+ d.AllowedContextTypes = []string{"t/ct/a@1.0.0", "t/ct/b@1.0.0"}
+ })
+ a := doc("contextType", "t/ct/a@1.0.0", nil)
+ b := doc("contextType", "t/ct/b@1.0.0", nil)
+ c := doc("contextType", "t/ct/c@1.0.0", nil)
+ objA := doc("context", "t/notes/a@1.0.0", func(d *resource.Document) { d.ContextType = "t/ct/a@1.0.0" })
+ agent := doc("agent", "t/agent@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/profile@1.0.0"}
+ d.AllowedContextTypes = []string{"t/ct/b@1.0.0", "t/ct/c@1.0.0"} // effective = [b]
+ d.Context = []string{"t/notes/a@1.0.0"} // type a, now excluded
+ })
+ _, err := New(docSet(cap, skill, profile, a, b, c, objA, agent)).Resolve("t/agent@1.0.0")
+ if err == nil || !strings.Contains(err.Error(), "not among the allowed") {
+ t.Fatalf("intersection of [a,b] and [b,c] should exclude type a, got %v", err)
+ }
+}
+
+func TestVariantContextRequirementRemainsAttachedToMode(t *testing.T) {
+ set := baseSkillAgent(func(s *resource.Document) {
+ s.Variants = map[string]resource.Variant{
+ "manual": {Instructions: "m"},
+ "a2a": {Instructions: "a", RequiresContextTypes: []string{"t/ct/gov@1.0.0"}},
+ }
+ }, nil) // agent holds no context
+ set["t/ct/gov@1.0.0"] = doc("contextType", "t/ct/gov@1.0.0", nil)
+ resolved, err := New(set).Resolve("t/agent@1.0.0")
+ if err != nil {
+ t.Fatalf("resolution must not flatten an unselected mode's context requirement: %v", err)
+ }
+ got := resolved.Skills[0].VariantContextRequirements["a2a"]
+ if len(got) != 1 || got[0] != "t/ct/gov@1.0.0" {
+ t.Fatalf("variant requirement was not preserved: %v", got)
+ }
+}
+
+func TestVariantToolRequirementAggregated(t *testing.T) {
+ set := baseSkillAgent(func(s *resource.Document) {
+ s.Variants = map[string]resource.Variant{
+ "pipeline": {Instructions: "p"},
+ "a2a": {Instructions: "a", RequiresTools: []string{"t/tools/reader@1.0.0"}},
+ }
+ }, nil)
+ // tool not declared -> error
+ _, err := New(set).Resolve("t/agent@1.0.0")
+ if err == nil || !strings.Contains(err.Error(), "requires tool") {
+ t.Fatalf("a variant's tool requirement must be enforced, got %v", err)
+ }
+}
diff --git a/go/internal/resolve/presence_test.go b/go/internal/resolve/presence_test.go
new file mode 100644
index 0000000..f49df19
--- /dev/null
+++ b/go/internal/resolve/presence_test.go
@@ -0,0 +1,133 @@
+package resolve
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/buchk/TypeFerence/go/internal/resource"
+)
+
+func capabilityRef(id string) *string { return &id }
+
+// mandateSetup: a governance profile that mandates capability C without binding
+// it — an abstract requirement its embedders must satisfy (ADR-0016).
+func mandateSetup() map[string]*resource.Document {
+ capC := doc("capability", "t/cap/c@1.0.0", nil)
+ control := doc("profile", "t/control@1.0.0", func(d *resource.Document) {
+ d.Skills = []resource.SkillBinding{{Capability: capabilityRef("t/cap/c@1.0.0"), Required: true}}
+ })
+ return docSet(capC, control)
+}
+
+func TestAbstractRequirementUnfulfilledIsCompileError(t *testing.T) {
+ set := mandateSetup()
+ set["t/agent@1.0.0"] = doc("agent", "t/agent@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/control@1.0.0"}
+ })
+ _, err := New(set).Resolve("t/agent@1.0.0")
+ if err == nil || !strings.Contains(err.Error(), "required") {
+ t.Fatalf("an agent embedding an unfulfilled mandate must not resolve, got %v", err)
+ }
+ if !strings.Contains(err.Error(), "t/control@1.0.0") {
+ t.Errorf("the error should name the mandating component, got %v", err)
+ }
+}
+
+func TestAbstractRequirementFulfilledByEmbedder(t *testing.T) {
+ set := mandateSetup()
+ set["t/skills/s@1.0.0"] = doc("skill", "t/skills/s@1.0.0", func(d *resource.Document) { d.Binds = "t/cap/c@1.0.0" })
+ set["t/agent@1.0.0"] = doc("agent", "t/agent@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/control@1.0.0"}
+ d.Skills = []resource.SkillBinding{{Ref: "t/skills/s@1.0.0"}}
+ })
+ resolved, err := New(set).Resolve("t/agent@1.0.0")
+ if err != nil {
+ t.Fatalf("binding the mandated capability must satisfy the requirement: %v", err)
+ }
+ if len(resolved.Skills) != 1 {
+ t.Fatalf("expected the fulfilling skill, got %d", len(resolved.Skills))
+ }
+ // The requirement, not the binding, decides mandatory-ness: this binding
+ // never said "required" itself.
+ if !resolved.Skills[0].Required {
+ t.Error("a skill fulfilling a mandate must be marked required in the resolved agent")
+ }
+}
+
+func TestProfileMayCarryUnfulfilledRequirement(t *testing.T) {
+ // A profile is abstract: it may mandate what it does not bind. Only agents
+ // are concrete and must satisfy every mandate.
+ set := mandateSetup()
+ resolved, err := New(set).resolveComponent("t/control@1.0.0", map[string]bool{}, false)
+ if err != nil {
+ t.Fatalf("a profile may carry an unfulfilled mandate: %v", err)
+ }
+ if len(resolved.RequiredCapabilities) != 1 || resolved.RequiredCapabilities[0] != "t/cap/c@1.0.0" {
+ t.Errorf("the profile should carry its mandate, got %v", resolved.RequiredCapabilities)
+ }
+ if len(resolved.Skills) != 0 {
+ t.Errorf("an abstract requirement binds nothing, got %d skills", len(resolved.Skills))
+ }
+}
+
+func TestRequirementPropagatesThroughIntermediateProfile(t *testing.T) {
+ set := mandateSetup()
+ set["t/mid@1.0.0"] = doc("profile", "t/mid@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/control@1.0.0"}
+ })
+ set["t/agent@1.0.0"] = doc("agent", "t/agent@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/mid@1.0.0"}
+ })
+ _, err := New(set).Resolve("t/agent@1.0.0")
+ if err == nil || !strings.Contains(err.Error(), "t/cap/c@1.0.0") {
+ t.Fatalf("a mandate must survive an intermediate profile, got %v", err)
+ }
+}
+
+func TestRequiredConcreteBindingMandatesDownstream(t *testing.T) {
+ // A concrete required binding both fulfills and mandates: an embedder that
+ // carries it inherits the obligation.
+ capC := doc("capability", "t/cap/c@1.0.0", nil)
+ s := doc("skill", "t/skills/s@1.0.0", func(d *resource.Document) { d.Binds = "t/cap/c@1.0.0" })
+ base := doc("profile", "t/base@1.0.0", func(d *resource.Document) {
+ d.Skills = []resource.SkillBinding{{Ref: "t/skills/s@1.0.0", Required: true}}
+ })
+ agent := doc("agent", "t/agent@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/base@1.0.0"}
+ })
+ resolved, err := New(docSet(capC, s, base, agent)).Resolve("t/agent@1.0.0")
+ if err != nil {
+ t.Fatalf("a promoted required binding is fulfilled by promotion: %v", err)
+ }
+ if len(resolved.RequiredCapabilities) != 1 {
+ t.Errorf("the mandate should propagate to the embedder, got %v", resolved.RequiredCapabilities)
+ }
+ if !resolved.Skills[0].Required {
+ t.Error("required must ride the skill through promotion")
+ }
+}
+
+func TestRequiredIsPresenceNotMutability(t *testing.T) {
+ // Overriding a required (but open) capability is legal: presence is
+ // preserved, and which skill fills it is sealing's concern, not this axis'.
+ capC := doc("capability", "t/cap/c@1.0.0", nil)
+ s1 := doc("skill", "t/skills/s1@1.0.0", func(d *resource.Document) { d.Binds = "t/cap/c@1.0.0" })
+ s2 := doc("skill", "t/skills/s2@1.0.0", func(d *resource.Document) { d.Binds = "t/cap/c@1.0.0" })
+ base := doc("profile", "t/base@1.0.0", func(d *resource.Document) {
+ d.Skills = []resource.SkillBinding{{Ref: "t/skills/s1@1.0.0", Required: true}}
+ })
+ agent := doc("agent", "t/agent@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/base@1.0.0"}
+ d.Skills = []resource.SkillBinding{{Ref: "t/skills/s2@1.0.0"}}
+ })
+ resolved, err := New(docSet(capC, s1, s2, base, agent)).Resolve("t/agent@1.0.0")
+ if err != nil {
+ t.Fatalf("required must not forbid override; that is sealed's job: %v", err)
+ }
+ if resolved.Skills[0].ImplementationID != "t/skills/s2@1.0.0" {
+ t.Errorf("the overriding skill should win, got %s", resolved.Skills[0].ImplementationID)
+ }
+ if !resolved.Skills[0].Required {
+ t.Error("the capability stays mandatory after a legal override")
+ }
+}
diff --git a/go/internal/resolve/resolver.go b/go/internal/resolve/resolver.go
index 93165f0..d5cd39a 100644
--- a/go/internal/resolve/resolver.go
+++ b/go/internal/resolve/resolver.go
@@ -5,7 +5,9 @@
package resolve
import (
+ "encoding/json"
"sort"
+ "strconv"
"strings"
"github.com/buchk/TypeFerence/go/internal/jsonx"
@@ -20,36 +22,76 @@ type ProvenanceEntry struct {
// ResolvedSkill is a concrete skill after promotion and contract checks.
type ResolvedSkill struct {
- DispatchName string
- CapabilityID string
- ImplementationID string
- Description string
- Instructions string
- InputSchema string
- OutputSchema string
- ContextFiles []string
- Provenance []ProvenanceEntry
+ DispatchName string
+ CapabilityID string
+ ImplementationID string
+ Description string
+ Instructions string
+ InputSchema string
+ OutputSchema string
+ ContextFiles []string
+ RequiresContextTypes []string
+ RequiresTools []string
+ // Exposed is true when the bound capability's visibility is "exposed":
+ // part of the agent's public callable surface, eligible for a callable
+ // card (ADR-0015). Rides the skill, so promotion carries it automatically.
+ Exposed bool
+ // Sealed marks a binding an embedder may not override or rebind; Required
+ // marks it mandatory (ADR-0016). Both ride the skill through promotion.
+ Sealed bool
+ Required bool
+ // Variants maps mode name to that mode's instructions for a multimodal
+ // skill (ADR-0012); nil for a unimodal skill. Instructions above holds the
+ // default (neutral) variant's rendering.
+ Variants map[string]string
+ // Variant requirements remain attached to their mode. Base requirements
+ // above apply to every mode.
+ VariantContextRequirements map[string][]string
+ VariantToolRequirements map[string][]string
+ Provenance []ProvenanceEntry
}
// ResolvedAgent is a fully composed agent or profile.
type ResolvedAgent struct {
- ID string
- DisplayName string
- Description string
- Emit bool
- Embeds []string
- Satisfies []string
- Slots map[string]string
- SlotKeys []string // canonical order for Slots
- WorkingNorms []string
- ContextFiles []string
- Skills []ResolvedSkill
- Provenance []ProvenanceEntry
+ ID string
+ DisplayName string
+ Description string
+ Emit bool
+ Embeds []string
+ Satisfies []string
+ Slots map[string]string
+ SlotKeys []string // canonical order for Slots
+ WorkingNorms []string
+ ContextFiles []string
+ Context []string
+ ContextObjects []ResolvedContextRef
+ AllowedContextTypes []string
+ HasAllowedContextTypes bool
+ Skills []ResolvedSkill
+ // RequiredCapabilities are the capability ids mandated by this component or
+ // anything it embeds. A profile may carry ones it does not bind (an abstract
+ // requirement); a resolved agent never can (ADR-0016).
+ RequiredCapabilities []string
+ Provenance []ProvenanceEntry
+}
+
+// ResolvedContextRef is a context object an agent holds by id, with the
+// contextType it instantiates and its materialized content (ADR-0013). Content
+// lets a target inline the held context, not merely reference it.
+type ResolvedContextRef struct {
+ ID string
+ DisplayName string
+ ContextType string
+ Satisfies []string
+ Content string
+ Values map[string]resource.FieldValue
+ ValuesJSON string
}
type interfaceContract struct {
- slots []string
- skills []string
+ slots []string
+ slotTypes map[string]string
+ skills []string
}
// Resolver composes resources into resolved agents.
@@ -85,6 +127,36 @@ func (r *Resolver) ResolveAll() ([]*ResolvedAgent, error) {
return nil, err
}
}
+ for _, id := range r.idsOfKind("contextType") {
+ if _, err := r.contextTypeClosure(id, map[string]bool{}); err != nil {
+ return nil, err
+ }
+ if r.resources[id].SchemaVersion == 4 {
+ if _, _, err := r.nativeContextShape(id); err != nil {
+ return nil, err
+ }
+ if err := r.validateContextValueTypeGraph(id, map[string]bool{}); err != nil {
+ return nil, err
+ }
+ if err := r.validateNativeDefaults(id); err != nil {
+ return nil, err
+ }
+ }
+ }
+ for _, id := range r.idsOfKind("context") {
+ obj := r.resources[id]
+ if _, err := r.contextTypeClosure(obj.ContextType, map[string]bool{}); err != nil {
+ return nil, resource.Errorf("%s: %s", id, err)
+ }
+ if err := r.validateContextFields(obj); err != nil {
+ return nil, err
+ }
+ }
+ for _, id := range r.idsOfKind("tool") {
+ if err := r.validateTool(r.resources[id]); err != nil {
+ return nil, err
+ }
+ }
for _, id := range r.idsOfKind("profile") {
if _, err := r.resolveComponent(id, map[string]bool{}, false); err != nil {
return nil, err
@@ -166,10 +238,36 @@ func (r *Resolver) resolveComponent(id string, visiting map[string]bool, require
}
norms := distinct(concatNorms(embedded, current))
contexts := distinct(normalizeAll(concatContexts(embedded, current)))
+ contextRefs := distinct(append(concatContextRefs(embedded, current), slotContextRefs(slots, slotKeys)...))
+ allowedContextTypes := intersectAllowLists(embedded, current)
skills, skillDepths, err := r.mergeSkills(id, current, embedded, contexts)
if err != nil {
return nil, err
}
+ requiredCapabilities, requiredBy, err := r.mergeRequired(id, current, embedded)
+ if err != nil {
+ return nil, err
+ }
+ // A mandated capability is mandatory wherever it is carried, however it came
+ // to be bound, so the flag reflects the requirement rather than the binding
+ // that happened to satisfy it (ADR-0016).
+ for capabilityID := range requiredBy {
+ if skill, bound := skills[capabilityID]; bound && !skill.Required {
+ skill.Required = true
+ skills[capabilityID] = skill
+ }
+ }
+ if current.Kind == "agent" {
+ if err := checkRequiredCapabilities(id, requiredBy, skills); err != nil {
+ return nil, err
+ }
+ if err := r.checkSkillDependencies(id, skills, contextRefs); err != nil {
+ return nil, err
+ }
+ if err := r.checkAllowedContext(id, contextRefs, allowedContextTypes, hasAllowedContextTypes(embedded, current)); err != nil {
+ return nil, err
+ }
+ }
satisfies := []string{}
for _, interfaceID := range r.idsOfKind("interface") {
@@ -177,7 +275,7 @@ func (r *Resolver) resolveComponent(id string, visiting map[string]bool, require
if ifErr != nil {
return nil, ifErr
}
- if satisfiesContract(contract, slots, skills) {
+ if r.satisfiesContract(contract, slots, skills) {
satisfies = append(satisfies, interfaceID)
}
}
@@ -218,28 +316,40 @@ func (r *Resolver) resolveComponent(id string, visiting map[string]bool, require
}
sortedSkills := make([]ResolvedSkill, 0, len(skills))
+ dispatchOwners := map[string]string{}
capabilityIDs := make([]string, 0, len(skills))
for capabilityID := range skills {
capabilityIDs = append(capabilityIDs, capabilityID)
}
sort.Strings(capabilityIDs)
for _, capabilityID := range capabilityIDs {
- sortedSkills = append(sortedSkills, withDispatch(skills[capabilityID], id))
+ skill := withDispatch(skills[capabilityID], id)
+ if prior, duplicate := dispatchOwners[skill.DispatchName]; duplicate {
+ return nil, resource.Errorf("%s: capabilities %s and %s produce the same target dispatch name %s",
+ id, prior, capabilityID, skill.DispatchName)
+ }
+ dispatchOwners[skill.DispatchName] = capabilityID
+ sortedSkills = append(sortedSkills, skill)
}
resolved := &ResolvedAgent{
- ID: id,
- DisplayName: displayName,
- Description: current.Description,
- Emit: current.Emit,
- Embeds: append([]string{}, current.Embeds...),
- Satisfies: satisfies,
- Slots: slots,
- SlotKeys: slotKeys,
- WorkingNorms: norms,
- ContextFiles: contexts,
- Skills: sortedSkills,
- Provenance: provenance,
+ ID: id,
+ DisplayName: displayName,
+ Description: current.Description,
+ Emit: current.Emit,
+ Embeds: append([]string{}, current.Embeds...),
+ Satisfies: satisfies,
+ Slots: slots,
+ SlotKeys: slotKeys,
+ WorkingNorms: norms,
+ ContextFiles: contexts,
+ Context: contextRefs,
+ ContextObjects: r.resolveContextObjects(contextRefs),
+ AllowedContextTypes: allowedContextTypes,
+ HasAllowedContextTypes: hasAllowedContextTypes(embedded, current),
+ Skills: sortedSkills,
+ RequiredCapabilities: requiredCapabilities,
+ Provenance: provenance,
}
r.slotDepths[id] = slotDepths
r.skillDepths[id] = skillDepths
@@ -319,6 +429,12 @@ type skillCandidate struct {
func (r *Resolver) mergeSkills(id string, current *resource.Document, embedded []*ResolvedAgent, contexts []string) (map[string]ResolvedSkill, map[string]int, error) {
localCapabilities := map[string]bool{}
for _, binding := range current.Skills {
+ // An abstract requirement is not a local binding: it neither resolves
+ // promotion ambiguity nor conflicts with a concrete binding of the same
+ // capability alongside it (mandate and fulfil in one place is legal).
+ if isBlank(binding.Ref) {
+ continue
+ }
capabilityID, err := r.resolveCapabilityID(binding, id)
if err != nil {
return nil, nil, err
@@ -346,6 +462,9 @@ func (r *Resolver) mergeSkills(id string, current *resource.Document, embedded [
result := map[string]ResolvedSkill{}
depths := map[string]int{}
+ // sealedBy records the embedded component that sealed a capability, so a
+ // shallower or local binding overriding it is a compile error (ADR-0016).
+ sealedBy := map[string]string{}
for _, capabilityID := range order {
group := candidates[capabilityID]
minDepth := group[0].depth
@@ -371,9 +490,26 @@ func (r *Resolver) mergeSkills(id string, current *resource.Document, embedded [
}
result[capabilityID] = nearest[0].skill
depths[capabilityID] = minDepth
+ // A sealed candidate may not be overridden by a shallower binding: the
+ // chosen (nearest) skill must be the sealed one itself.
+ for _, c := range group {
+ if c.skill.Sealed {
+ sealedBy[capabilityID] = c.agent
+ if result[capabilityID].ImplementationID != c.skill.ImplementationID {
+ return nil, nil, resource.Errorf(
+ "%s: capability '%s' is sealed by %s and cannot be overridden",
+ id, capabilityID, c.agent)
+ }
+ }
+ }
}
for _, binding := range current.Skills {
+ // An abstract requirement fulfills nothing; it only mandates that some
+ // component bind the capability. Presence is checked after the merge.
+ if isBlank(binding.Ref) {
+ continue
+ }
implementation, err := r.require(binding.Ref, "skill")
if err != nil {
return nil, nil, err
@@ -382,6 +518,11 @@ func (r *Resolver) mergeSkills(id string, current *resource.Document, embedded [
if err != nil {
return nil, nil, err
}
+ if source, sealed := sealedBy[capabilityID]; sealed {
+ return nil, nil, resource.Errorf(
+ "%s: capability '%s' is sealed by %s and cannot be rebound",
+ id, capabilityID, source)
+ }
capability, err := r.require(capabilityID, "capability")
if err != nil {
return nil, nil, err
@@ -402,14 +543,27 @@ func (r *Resolver) mergeSkills(id string, current *resource.Document, embedded [
if err != nil {
return nil, nil, err
}
+ instructions := implementation.Instructions
+ defaultInstructions, variants := resolveVariants(implementation.Variants)
+ if variants != nil {
+ instructions = defaultInstructions
+ }
result[capabilityID] = ResolvedSkill{
- CapabilityID: capabilityID,
- ImplementationID: implementation.ID,
- Description: implementation.Description,
- Instructions: implementation.Instructions,
- InputSchema: inputSchema,
- OutputSchema: outputSchema,
- ContextFiles: distinct(append(append([]string{}, contexts...), normalizeAll(implementation.ContextFiles)...)),
+ CapabilityID: capabilityID,
+ ImplementationID: implementation.ID,
+ Description: implementation.Description,
+ Instructions: instructions,
+ Variants: variants,
+ InputSchema: inputSchema,
+ OutputSchema: outputSchema,
+ ContextFiles: distinct(append(append([]string{}, contexts...), normalizeAll(implementation.ContextFiles)...)),
+ RequiresContextTypes: append([]string{}, implementation.RequiresContextTypes...),
+ RequiresTools: append([]string{}, implementation.RequiresTools...),
+ VariantContextRequirements: variantContextRequirements(implementation),
+ VariantToolRequirements: variantToolRequirements(implementation),
+ Exposed: capability.Visibility == "exposed",
+ Sealed: binding.Sealed,
+ Required: binding.Required,
Provenance: []ProvenanceEntry{
{Field: "skill.capability", Source: capabilityID},
{Field: "skill.implementation", Source: implementation.ID},
@@ -420,6 +574,61 @@ func (r *Resolver) mergeSkills(id string, current *resource.Document, embedded [
return result, depths, nil
}
+// mergeRequired computes the capability ids mandated for this component: those
+// its embedded components mandate, plus those it marks required itself. A
+// binding may mandate a capability without fulfilling it (no ref), which is how
+// a profile demands a control its embedders must supply (ADR-0016). Returns the
+// canonical id list and a capability -> mandating component map for diagnostics.
+func (r *Resolver) mergeRequired(id string, current *resource.Document, embedded []*ResolvedAgent) ([]string, map[string]string, error) {
+ requiredBy := map[string]string{}
+ for _, component := range embedded {
+ for _, capabilityID := range component.RequiredCapabilities {
+ if _, seen := requiredBy[capabilityID]; !seen {
+ requiredBy[capabilityID] = component.ID
+ }
+ }
+ }
+ for _, binding := range current.Skills {
+ if !binding.Required {
+ continue
+ }
+ capabilityID := ""
+ if isBlank(binding.Ref) {
+ capabilityID = *binding.Capability
+ } else {
+ resolved, err := r.resolveCapabilityID(binding, id)
+ if err != nil {
+ return nil, nil, err
+ }
+ capabilityID = resolved
+ }
+ if _, err := r.require(capabilityID, "capability"); err != nil {
+ return nil, nil, err
+ }
+ requiredBy[capabilityID] = id
+ }
+ return sortedStringMapKeys(requiredBy), requiredBy, nil
+}
+
+// checkRequiredCapabilities verifies every mandated capability is bound after
+// composition. A profile MAY carry unfulfilled requirements — that is precisely
+// what makes it abstract — but an agent is concrete and must satisfy them all.
+// Presence is the axis: what binds the capability is sealing's concern, not this
+// check's (ADR-0016).
+func checkRequiredCapabilities(agentID string, requiredBy map[string]string, skills map[string]ResolvedSkill) error {
+ for _, capabilityID := range sortedStringMapKeys(requiredBy) {
+ if _, bound := skills[capabilityID]; bound {
+ continue
+ }
+ if source := requiredBy[capabilityID]; source != agentID {
+ return resource.Errorf("%s: capability '%s' is required by %s, but no skill binds it; bind it on %s",
+ agentID, capabilityID, source, agentID)
+ }
+ return resource.Errorf("%s: capability '%s' is required, but no skill binds it", agentID, capabilityID)
+ }
+ return nil
+}
+
func (r *Resolver) resolveCapabilityID(binding resource.SkillBinding, agent string) (string, error) {
implementation, err := r.require(binding.Ref, "skill")
if err != nil {
@@ -464,6 +673,7 @@ func (r *Resolver) resolveInterface(id string, visiting map[string]bool) (*inter
defer delete(visiting, id)
slots := []string{}
+ slotTypes := map[string]string{}
skills := []string{}
for _, embedID := range current.Embeds {
embedded, embErr := r.resolveInterface(embedID, visiting)
@@ -471,6 +681,12 @@ func (r *Resolver) resolveInterface(id string, visiting map[string]bool) (*inter
return nil, embErr
}
slots = append(slots, embedded.slots...)
+ for name, contextType := range embedded.slotTypes {
+ if prior, exists := slotTypes[name]; exists && prior != contextType {
+ return nil, resource.Errorf("%s: embedded interfaces require incompatible context types for slot %s", id, name)
+ }
+ slotTypes[name] = contextType
+ }
skills = append(skills, embedded.skills...)
}
for _, capability := range current.RequiresCapabilities {
@@ -478,19 +694,37 @@ func (r *Resolver) resolveInterface(id string, visiting map[string]bool) (*inter
return nil, capErr
}
}
+ for name, contextType := range current.RequiredSlotTypes {
+ if _, err := r.require(contextType, "contextType"); err != nil {
+ return nil, err
+ }
+ slotTypes[name] = contextType
+ }
contract := &interfaceContract{
- slots: distinct(append(slots, current.RequiresSlots...)),
- skills: distinct(append(skills, current.RequiresCapabilities...)),
+ slots: distinct(append(slots, current.RequiresSlots...)),
+ slotTypes: slotTypes,
+ skills: distinct(append(skills, current.RequiresCapabilities...)),
}
r.interfaceCache[id] = contract
return contract, nil
}
-func satisfiesContract(contract *interfaceContract, slots map[string]string, skills map[string]ResolvedSkill) bool {
+func (r *Resolver) satisfiesContract(contract *interfaceContract, slots map[string]string, skills map[string]ResolvedSkill) bool {
for _, slot := range contract.slots {
- if _, ok := slots[slot]; !ok {
+ value, ok := slots[slot]
+ if !ok {
return false
}
+ if requiredType := contract.slotTypes[slot]; requiredType != "" {
+ context, exists := r.resources[value]
+ if !exists || context.Kind != "context" {
+ return false
+ }
+ closure, err := r.contextTypeClosure(context.ContextType, map[string]bool{})
+ if err != nil || !contains(closure, requiredType) {
+ return false
+ }
+ }
}
for _, skill := range contract.skills {
if _, ok := skills[skill]; !ok {
@@ -500,6 +734,732 @@ func satisfiesContract(contract *interfaceContract, slots map[string]string, ski
return true
}
+func contains(values []string, wanted string) bool {
+ for _, value := range values {
+ if value == wanted {
+ return true
+ }
+ }
+ return false
+}
+
+// contextTypeClosure returns the set of contextType ids a context object of the
+// given type satisfies: the type itself plus every type it transitively embeds
+// (refinement). A governedX that embeds X satisfies both (ADR-0013).
+func (r *Resolver) contextTypeClosure(id string, visiting map[string]bool) ([]string, error) {
+ ct, ok := r.resources[id]
+ if !ok || ct.Kind != "contextType" {
+ return nil, resource.Errorf("Missing contextType: %s", id)
+ }
+ if visiting[id] {
+ return nil, resource.Errorf("ContextType refinement cycle detected at %s", id)
+ }
+ visiting[id] = true
+ defer delete(visiting, id)
+ result := []string{id}
+ for _, embedID := range ct.Embeds {
+ base, err := r.contextTypeClosure(embedID, visiting)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, base...)
+ }
+ return distinct(result), nil
+}
+
+// providedContextTypes is the union of contextType closures over the context
+// objects an agent holds by id.
+func (r *Resolver) providedContextTypes(objectIDs []string) (map[string]bool, error) {
+ provided := map[string]bool{}
+ for _, objID := range objectIDs {
+ obj, ok := r.resources[objID]
+ if !ok || obj.Kind != "context" {
+ return nil, resource.Errorf("Missing context: %s", objID)
+ }
+ closure, err := r.contextTypeClosure(obj.ContextType, map[string]bool{})
+ if err != nil {
+ return nil, err
+ }
+ for _, t := range closure {
+ provided[t] = true
+ }
+ }
+ return provided, nil
+}
+
+// validateContextFields checks a context object against its contextType schema
+// (and every type it refines): required fields are present, and each declared
+// field's structural type matches (ADR-0013).
+func (r *Resolver) validateContextFields(obj *resource.Document) error {
+ if obj.SchemaVersion == 4 {
+ return r.validateNativeContext(obj)
+ }
+ closure, err := r.contextTypeClosure(obj.ContextType, map[string]bool{})
+ if err != nil {
+ return err
+ }
+ required := map[string]bool{}
+ propTypes := map[string]string{}
+ for _, ctID := range closure {
+ req, types, err := parseContextSchema(r.resources[ctID].Schema)
+ if err != nil {
+ return resource.Errorf("%s: contextType %s has an invalid schema: %s", obj.ID, ctID, err)
+ }
+ for _, field := range req {
+ required[field] = true
+ }
+ for field, t := range types {
+ propTypes[field] = t
+ }
+ }
+ for _, name := range sortedStringSet(required) {
+ if _, ok := obj.ContextFields[name]; !ok {
+ return resource.Errorf("%s: missing required field %q declared by its contextType schema", obj.ID, name)
+ }
+ }
+ for _, name := range sortedStringMapKeys(propTypes) {
+ field, ok := obj.ContextFields[name]
+ if !ok {
+ continue // absent optional field; presence is handled above
+ }
+ if !kindMatchesType(field.Kind, propTypes[name]) {
+ return resource.Errorf("%s: field %q must be %s per its contextType schema, got a %s",
+ obj.ID, name, propTypes[name], field.Kind)
+ }
+ }
+ return nil
+}
+
+// parseContextSchema reads a JSON Schema's top-level "required" names and
+// property types. A malformed "required" or "properties" is an error rather
+// than a silently-empty result.
+func parseContextSchema(schema string) ([]string, map[string]string, error) {
+ if strings.TrimSpace(schema) == "" {
+ return nil, nil, nil
+ }
+ var doc struct {
+ Required []string `json:"required"`
+ Properties map[string]struct {
+ Type string `json:"type"`
+ } `json:"properties"`
+ }
+ if err := json.Unmarshal([]byte(schema), &doc); err != nil {
+ return nil, nil, resource.Errorf("\"required\" must be an array of field names and \"properties\" a type map")
+ }
+ types := map[string]string{}
+ for name, prop := range doc.Properties {
+ if prop.Type != "" {
+ types[name] = prop.Type
+ }
+ }
+ return doc.Required, types, nil
+}
+
+// kindMatchesType reports whether a field's structural kind satisfies a JSON
+// Schema type. Unknown/absent types are not enforced.
+func kindMatchesType(kind, schemaType string) bool {
+ switch schemaType {
+ case "array":
+ return kind == "sequence"
+ case "object":
+ return kind == "mapping"
+ case "string", "number", "integer", "boolean":
+ return kind == "scalar"
+ default:
+ return true
+ }
+}
+
+func sortedStringSet(m map[string]bool) []string {
+ keys := make([]string, 0, len(m))
+ for k := range m {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ return keys
+}
+
+func sortedStringMapKeys(m map[string]string) []string {
+ keys := make([]string, 0, len(m))
+ for k := range m {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ return keys
+}
+
+// validateTool checks a tool declaration's interface schemas parse (ADR-0017).
+func (r *Resolver) validateTool(tool *resource.Document) error {
+ if _, err := canonicalJSON(tool.InputSchema); err != nil {
+ return resource.Errorf("%s: invalid tool inputSchema: %s", tool.ID, err)
+ }
+ if _, err := canonicalJSON(tool.OutputSchema); err != nil {
+ return resource.Errorf("%s: invalid tool outputSchema: %s", tool.ID, err)
+ }
+ return nil
+}
+
+// checkSkillDependencies verifies, at agent level, that every resolved skill's
+// required context types are provided by held context and its required tools are
+// declared (ADR-0013, ADR-0017).
+func (r *Resolver) checkSkillDependencies(agentID string, skills map[string]ResolvedSkill, contextRefs []string) error {
+ provided, err := r.providedContextTypes(contextRefs)
+ if err != nil {
+ return err
+ }
+ capabilityIDs := make([]string, 0, len(skills))
+ for capabilityID := range skills {
+ capabilityIDs = append(capabilityIDs, capabilityID)
+ }
+ sort.Strings(capabilityIDs)
+ for _, capabilityID := range capabilityIDs {
+ skill := skills[capabilityID]
+ for _, required := range skill.RequiresContextTypes {
+ if !provided[required] {
+ return resource.Errorf("%s: skill %s requires context type %s, which no held context provides",
+ agentID, skill.ImplementationID, required)
+ }
+ }
+ for _, toolID := range skill.RequiresTools {
+ if _, err := r.require(toolID, "tool"); err != nil {
+ return resource.Errorf("%s: skill %s requires tool %s, which is not declared",
+ agentID, skill.ImplementationID, toolID)
+ }
+ }
+ for _, mode := range sortedRequirementModes(skill) {
+ for _, required := range skill.VariantContextRequirements[mode] {
+ if _, err := r.require(required, "contextType"); err != nil {
+ return resource.Errorf("%s: skill %s variant %s requires context type %s, which is not declared",
+ agentID, skill.ImplementationID, mode, required)
+ }
+ }
+ for _, toolID := range skill.VariantToolRequirements[mode] {
+ if _, err := r.require(toolID, "tool"); err != nil {
+ return resource.Errorf("%s: skill %s variant %s requires tool %s, which is not declared",
+ agentID, skill.ImplementationID, mode, toolID)
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// resolveVariants turns authored variants into a mode->instructions map and
+// selects the default (neutral) rendering. The default preference is
+// pipeline > manual > a2a, falling back to the alphabetically-first mode; a
+// target adapter may later select a surface-appropriate variant (ADR-0012).
+func resolveVariants(v map[string]resource.Variant) (string, map[string]string) {
+ if len(v) == 0 {
+ return "", nil
+ }
+ resolved := map[string]string{}
+ names := make([]string, 0, len(v))
+ for name := range v {
+ resolved[name] = v[name].Instructions
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ for _, pref := range []string{"pipeline", "manual", "a2a"} {
+ if ins, ok := resolved[pref]; ok {
+ return ins, resolved
+ }
+ }
+ return resolved[names[0]], resolved
+}
+
+func variantContextRequirements(impl *resource.Document) map[string][]string {
+ if len(impl.Variants) == 0 {
+ return nil
+ }
+ result := map[string][]string{}
+ for _, mode := range sortedKeys(impl.Variants) {
+ result[mode] = distinct(append([]string{}, impl.Variants[mode].RequiresContextTypes...))
+ }
+ return result
+}
+
+func (r *Resolver) validateNativeContext(obj *resource.Document) error {
+ fields, bodyRequired, err := r.nativeContextShape(obj.ContextType)
+ if err != nil {
+ return resource.Errorf("%s: %s", obj.ID, err)
+ }
+ if obj.ContextFields == nil {
+ obj.ContextFields = map[string]resource.FieldValue{}
+ }
+ for name := range obj.ContextFields {
+ if _, declared := fields[name]; !declared {
+ return resource.Errorf("%s: unknown context field %q", obj.ID, name)
+ }
+ }
+ for _, name := range sortedContextFieldKeys(fields) {
+ field := fields[name]
+ value, present := obj.ContextFields[name]
+ if !present && field.HasDefault {
+ value = cloneFieldValue(field.Default)
+ obj.ContextFields[name] = value
+ present = true
+ }
+ if !present {
+ if field.Required {
+ return resource.Errorf("%s: missing required context field %q", obj.ID, name)
+ }
+ continue
+ }
+ materialized, err := r.materializeNativeValue(value, field.Type, name)
+ if err != nil {
+ return resource.Errorf("%s: %s", obj.ID, err)
+ }
+ obj.ContextFields[name] = materialized
+ }
+ if bodyRequired && strings.TrimSpace(obj.Content) == "" {
+ return resource.Errorf("%s: its contextType requires a text body", obj.ID)
+ }
+ return nil
+}
+
+func (r *Resolver) nativeContextShape(id string) (map[string]resource.ContextField, bool, error) {
+ closure, err := r.contextTypeClosure(id, map[string]bool{})
+ if err != nil {
+ return nil, false, err
+ }
+ fields := map[string]resource.ContextField{}
+ bodyRequired := false
+ // Closure is derived-first. Apply bases first so a valid refinement can
+ // strengthen optional -> required.
+ for i := len(closure) - 1; i >= 0; i-- {
+ ct := r.resources[closure[i]]
+ if ct.SchemaVersion != 4 {
+ return nil, false, resource.Errorf("native contextType %s cannot refine legacy schemaVersion %d type %s", id, ct.SchemaVersion, ct.ID)
+ }
+ if ct.ContextBody != nil && ct.ContextBody.Required {
+ bodyRequired = true
+ }
+ for _, name := range sortedContextFieldKeys(ct.ContextTypeFields) {
+ next := ct.ContextTypeFields[name]
+ if prior, exists := fields[name]; exists {
+ if !sameTypeExpr(prior.Type, next.Type) {
+ return nil, false, resource.Errorf("contextType %s changes inherited field %q's type", ct.ID, name)
+ }
+ if prior.Required && !next.Required {
+ return nil, false, resource.Errorf("contextType %s weakens inherited required field %q", ct.ID, name)
+ }
+ if prior.HasDefault && !next.HasDefault {
+ next.HasDefault = true
+ next.Default = cloneFieldValue(prior.Default)
+ }
+ }
+ fields[name] = next
+ }
+ }
+ return fields, bodyRequired, nil
+}
+
+func (r *Resolver) validateNativeValue(value resource.FieldValue, typ resource.TypeExpr, field string) error {
+ _, err := r.materializeNativeValue(value, typ, field)
+ return err
+}
+
+func (r *Resolver) materializeNativeValue(value resource.FieldValue, typ resource.TypeExpr, field string) (resource.FieldValue, error) {
+ switch typ.Kind {
+ case "string", "text":
+ if value.Kind != "scalar" {
+ return value, resource.Errorf("field %q must be %s, got %s", field, typ.Kind, value.Kind)
+ }
+ case "boolean":
+ if value.Kind != "scalar" || (value.Scalar != "true" && value.Scalar != "false") {
+ return value, resource.Errorf("field %q must be boolean true or false", field)
+ }
+ case "integer":
+ if value.Kind != "scalar" {
+ return value, resource.Errorf("field %q must be an integer", field)
+ }
+ if _, err := strconv.ParseInt(value.Scalar, 10, 64); err != nil {
+ return value, resource.Errorf("field %q must be an integer", field)
+ }
+ if parsed, err := jsonx.Parse(value.Scalar); err != nil {
+ return value, resource.Errorf("field %q must use a canonical JSON integer token", field)
+ } else if _, ok := parsed.(jsonx.Num); !ok || strings.ContainsAny(value.Scalar, ".eE") {
+ return value, resource.Errorf("field %q must be an integer", field)
+ }
+ case "number":
+ if value.Kind != "scalar" {
+ return value, resource.Errorf("field %q must be a number", field)
+ }
+ if _, err := strconv.ParseFloat(value.Scalar, 64); err != nil {
+ return value, resource.Errorf("field %q must be a number", field)
+ }
+ if parsed, err := jsonx.Parse(value.Scalar); err != nil {
+ return value, resource.Errorf("field %q must use a canonical JSON number token", field)
+ } else if _, ok := parsed.(jsonx.Num); !ok {
+ return value, resource.Errorf("field %q must be a number", field)
+ }
+ case "list":
+ if value.Kind != "list" {
+ return value, resource.Errorf("field %q must be a list", field)
+ }
+ for i, item := range value.List {
+ materialized, err := r.materializeNativeValue(item, *typ.Elem, field+"["+strconv.Itoa(i)+"]")
+ if err != nil {
+ return value, err
+ }
+ value.List[i] = materialized
+ }
+ case "map":
+ if value.Kind != "map" {
+ return value, resource.Errorf("field %q must be a map", field)
+ }
+ for _, key := range sortedFieldValueKeys(value.Map) {
+ materialized, err := r.materializeNativeValue(value.Map[key], *typ.Elem, field+"."+key)
+ if err != nil {
+ return value, err
+ }
+ value.Map[key] = materialized
+ }
+ case "ref":
+ if value.Kind != "map" {
+ return value, resource.Errorf("field %q must be a value of named context type %s", field, typ.Ref)
+ }
+ fields, bodyRequired, err := r.nativeContextShape(typ.Ref)
+ if err != nil {
+ return value, err
+ }
+ if bodyRequired {
+ return value, resource.Errorf("field %q cannot embed named context type %s because it requires a text body", field, typ.Ref)
+ }
+ for key := range value.Map {
+ if _, ok := fields[key]; !ok {
+ return value, resource.Errorf("field %q has unknown member %q for %s", field, key, typ.Ref)
+ }
+ }
+ for _, key := range sortedContextFieldKeys(fields) {
+ member, present := value.Map[key]
+ if !present && fields[key].HasDefault {
+ member = cloneFieldValue(fields[key].Default)
+ present = true
+ }
+ if !present {
+ if fields[key].Required {
+ return value, resource.Errorf("field %q is missing required member %q", field, key)
+ }
+ continue
+ }
+ materialized, err := r.materializeNativeValue(member, fields[key].Type, field+"."+key)
+ if err != nil {
+ return value, err
+ }
+ value.Map[key] = materialized
+ }
+ default:
+ return value, resource.Errorf("field %q uses unsupported type %q", field, typ.Kind)
+ }
+ return value, nil
+}
+
+func (r *Resolver) validateNativeDefaults(id string) error {
+ fields, _, err := r.nativeContextShape(id)
+ if err != nil {
+ return err
+ }
+ for _, name := range sortedContextFieldKeys(fields) {
+ field := fields[name]
+ if !field.HasDefault {
+ continue
+ }
+ if _, err := r.materializeNativeValue(cloneFieldValue(field.Default), field.Type, name+" default"); err != nil {
+ return resource.Errorf("contextType %s: %s", id, err)
+ }
+ }
+ return nil
+}
+
+func (r *Resolver) validateContextValueTypeGraph(id string, visiting map[string]bool) error {
+ if visiting[id] {
+ return resource.Errorf("named context value type cycle detected at %s", id)
+ }
+ visiting[id] = true
+ defer delete(visiting, id)
+ fields, _, err := r.nativeContextShape(id)
+ if err != nil {
+ return err
+ }
+ for _, name := range sortedContextFieldKeys(fields) {
+ for _, ref := range typeExprRefs(fields[name].Type) {
+ if err := r.validateContextValueTypeGraph(ref, visiting); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func typeExprRefs(typ resource.TypeExpr) []string {
+ if typ.Kind == "ref" {
+ return []string{typ.Ref}
+ }
+ if typ.Elem != nil {
+ return typeExprRefs(*typ.Elem)
+ }
+ return nil
+}
+
+func sameTypeExpr(a, b resource.TypeExpr) bool {
+ if a.Kind != b.Kind || a.Ref != b.Ref || (a.Elem == nil) != (b.Elem == nil) {
+ return false
+ }
+ return a.Elem == nil || sameTypeExpr(*a.Elem, *b.Elem)
+}
+
+func sortedContextFieldKeys(values map[string]resource.ContextField) []string {
+ keys := make([]string, 0, len(values))
+ for key := range values {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+ return keys
+}
+
+func sortedFieldValueKeys(values map[string]resource.FieldValue) []string {
+ keys := make([]string, 0, len(values))
+ for key := range values {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+ return keys
+}
+
+func variantToolRequirements(impl *resource.Document) map[string][]string {
+ if len(impl.Variants) == 0 {
+ return nil
+ }
+ result := map[string][]string{}
+ for _, mode := range sortedKeys(impl.Variants) {
+ result[mode] = distinct(append([]string{}, impl.Variants[mode].RequiresTools...))
+ }
+ return result
+}
+
+func sortedRequirementModes(skill ResolvedSkill) []string {
+ set := map[string]bool{}
+ for mode := range skill.VariantContextRequirements {
+ set[mode] = true
+ }
+ for mode := range skill.VariantToolRequirements {
+ set[mode] = true
+ }
+ return sortedStringSet(set)
+}
+
+func sortedKeys(m map[string]resource.Variant) []string {
+ keys := make([]string, 0, len(m))
+ for k := range m {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ return keys
+}
+
+// intersectAllowLists computes the effective allowedContextTypes for a
+// component: the intersection of every non-empty allow-list among itself and
+// its embedded components (empty result means unrestricted; ADR-0013).
+func intersectAllowLists(embedded []*ResolvedAgent, current *resource.Document) []string {
+ lists := [][]string{}
+ for _, c := range embedded {
+ if c.HasAllowedContextTypes || len(c.AllowedContextTypes) > 0 {
+ lists = append(lists, c.AllowedContextTypes)
+ }
+ }
+ if current.HasAllowedContextTypes || len(current.AllowedContextTypes) > 0 {
+ lists = append(lists, current.AllowedContextTypes)
+ }
+ if len(lists) == 0 {
+ return nil
+ }
+ result := append([]string{}, lists[0]...)
+ for _, l := range lists[1:] {
+ set := map[string]bool{}
+ for _, x := range l {
+ set[x] = true
+ }
+ filtered := result[:0:0]
+ for _, x := range result {
+ if set[x] {
+ filtered = append(filtered, x)
+ }
+ }
+ result = filtered
+ }
+ return distinct(result)
+}
+
+func hasAllowedContextTypes(embedded []*ResolvedAgent, current *resource.Document) bool {
+ if current.HasAllowedContextTypes || len(current.AllowedContextTypes) > 0 {
+ return true
+ }
+ for _, component := range embedded {
+ if component.HasAllowedContextTypes || len(component.AllowedContextTypes) > 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// checkAllowedContext verifies each held context object satisfies at least one
+// allowed contextType. An explicit empty list is deny-all; omission is
+// unrestricted.
+func (r *Resolver) checkAllowedContext(agentID string, contextRefs, allowed []string, constrained bool) error {
+ if !constrained {
+ return nil
+ }
+ allowSet := map[string]bool{}
+ for _, a := range allowed {
+ allowSet[a] = true
+ }
+ for _, objID := range contextRefs {
+ obj, ok := r.resources[objID]
+ if !ok || obj.Kind != "context" {
+ continue // existence is checked in providedContextTypes
+ }
+ closure, err := r.contextTypeClosure(obj.ContextType, map[string]bool{})
+ if err != nil {
+ return err
+ }
+ permitted := false
+ for _, t := range closure {
+ if allowSet[t] {
+ permitted = true
+ break
+ }
+ }
+ if !permitted {
+ return resource.Errorf("%s: held context %s (type %s) is not among the allowed context types",
+ agentID, objID, obj.ContextType)
+ }
+ }
+ return nil
+}
+
+// resolveContextObjects turns held context ids into (id, contextType) refs,
+// sorted by id for deterministic emission. Ids that do not resolve to a context
+// object are skipped (agent-level checks surface missing context elsewhere).
+func (r *Resolver) resolveContextObjects(contextRefs []string) []ResolvedContextRef {
+ refs := []ResolvedContextRef{}
+ for _, id := range contextRefs {
+ if obj, ok := r.resources[id]; ok && obj.Kind == "context" {
+ satisfies, _ := r.contextTypeClosure(obj.ContextType, map[string]bool{})
+ refs = append(refs, ResolvedContextRef{
+ ID: id,
+ DisplayName: obj.DisplayName,
+ ContextType: obj.ContextType,
+ Satisfies: satisfies,
+ Content: obj.Content,
+ Values: cloneFieldValues(obj.ContextFields),
+ ValuesJSON: r.contextValuesJSON(obj),
+ })
+ }
+ }
+ sort.Slice(refs, func(i, j int) bool { return refs[i].ID < refs[j].ID })
+ return refs
+}
+
+func (r *Resolver) contextValuesJSON(obj *resource.Document) string {
+ if obj.SchemaVersion != 4 {
+ return "{}"
+ }
+ fields, _, err := r.nativeContextShape(obj.ContextType)
+ if err != nil {
+ return "{}"
+ }
+ out := jsonx.Obj{}
+ for _, name := range sortedContextFieldKeys(fields) {
+ value, present := obj.ContextFields[name]
+ if !present {
+ continue
+ }
+ out = append(out, jsonx.Member{K: name, V: r.nativeValueJSON(value, fields[name].Type)})
+ }
+ return jsonx.Compact(out)
+}
+
+func (r *Resolver) nativeValueJSON(value resource.FieldValue, typ resource.TypeExpr) jsonx.Value {
+ switch typ.Kind {
+ case "boolean":
+ return jsonx.Bool(value.Scalar == "true")
+ case "integer", "number":
+ parsed, err := jsonx.Parse(value.Scalar)
+ if err == nil {
+ return parsed
+ }
+ return jsonx.Str(value.Scalar)
+ case "list":
+ arr := jsonx.Arr{}
+ for _, item := range value.List {
+ arr = append(arr, r.nativeValueJSON(item, *typ.Elem))
+ }
+ return arr
+ case "map":
+ obj := jsonx.Obj{}
+ for _, key := range sortedFieldValueKeys(value.Map) {
+ obj = append(obj, jsonx.Member{K: key, V: r.nativeValueJSON(value.Map[key], *typ.Elem)})
+ }
+ return obj
+ case "ref":
+ fields, _, err := r.nativeContextShape(typ.Ref)
+ if err != nil {
+ return jsonx.Obj{}
+ }
+ obj := jsonx.Obj{}
+ for _, key := range sortedFieldValueKeys(value.Map) {
+ obj = append(obj, jsonx.Member{K: key, V: r.nativeValueJSON(value.Map[key], fields[key].Type)})
+ }
+ return obj
+ default:
+ return jsonx.Str(value.Scalar)
+ }
+}
+
+func cloneFieldValues(values map[string]resource.FieldValue) map[string]resource.FieldValue {
+ if values == nil {
+ return nil
+ }
+ out := make(map[string]resource.FieldValue, len(values))
+ for key, value := range values {
+ out[key] = cloneFieldValue(value)
+ }
+ return out
+}
+
+func cloneFieldValue(value resource.FieldValue) resource.FieldValue {
+ cloned := resource.FieldValue{Kind: value.Kind, Scalar: value.Scalar}
+ if value.List != nil {
+ cloned.List = make([]resource.FieldValue, len(value.List))
+ for i, item := range value.List {
+ cloned.List[i] = cloneFieldValue(item)
+ }
+ }
+ if value.Map != nil {
+ cloned.Map = cloneFieldValues(value.Map)
+ }
+ return cloned
+}
+
+func concatContextRefs(embedded []*ResolvedAgent, current *resource.Document) []string {
+ values := []string{}
+ for _, component := range embedded {
+ values = append(values, component.Context...)
+ }
+ return append(values, current.Context...)
+}
+
+func slotContextRefs(slots map[string]string, keys []string) []string {
+ refs := []string{}
+ for _, key := range keys {
+ if resource.IsResourceID(slots[key]) {
+ refs = append(refs, slots[key])
+ }
+ }
+ return refs
+}
+
func (r *Resolver) require(id, kind string) (*resource.Document, error) {
doc, ok := r.resources[id]
if !ok || doc.Kind != kind {
@@ -565,6 +1525,30 @@ func withDispatch(skill ResolvedSkill, agentID string) ResolvedSkill {
return skill
}
+// InstructionsFor returns the instructions for an invocation mode: the variant's
+// rendering when this is a multimodal skill that declares the mode, otherwise the
+// default Instructions (ADR-0012). Lets a surface pick its face — e.g. a callable
+// card selects the a2a variant.
+func (s ResolvedSkill) InstructionsFor(mode string) string {
+ if ins, ok := s.Variants[mode]; ok {
+ return ins
+ }
+ return s.Instructions
+}
+
+// ExposedSkills returns the resolved skills whose capability is exposed, in
+// dispatch order: the agent's public callable surface (ADR-0015). A callable
+// card (ADR-0018) is emitted from exactly these, not from every skill.
+func (a *ResolvedAgent) ExposedSkills() []ResolvedSkill {
+ out := []ResolvedSkill{}
+ for _, s := range a.Skills {
+ if s.Exposed {
+ out = append(out, s)
+ }
+ }
+ return out
+}
+
// Leaf extracts the unversioned name segment of a resource id
// (namespace/name@version -> name).
func Leaf(id string) string {
diff --git a/go/internal/resolve/schema_fields_test.go b/go/internal/resolve/schema_fields_test.go
new file mode 100644
index 0000000..e6d41ef
--- /dev/null
+++ b/go/internal/resolve/schema_fields_test.go
@@ -0,0 +1,82 @@
+package resolve
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/buchk/TypeFerence/go/internal/resource"
+)
+
+func TestContextMissingRequiredFieldRejected(t *testing.T) {
+ ct := doc("contextType", "t/ct/cast@1.0.0", func(d *resource.Document) {
+ d.Schema = `{"type":"object","required":["role"]}`
+ })
+ obj := doc("context", "t/notes/n@1.0.0", func(d *resource.Document) { d.ContextType = "t/ct/cast@1.0.0" })
+ _, err := New(docSet(ct, obj)).ResolveAll()
+ if err == nil || !strings.Contains(err.Error(), "missing required field") {
+ t.Fatalf("expected a missing-required-field error, got %v", err)
+ }
+}
+
+func TestContextRequiredFieldPresent(t *testing.T) {
+ ct := doc("contextType", "t/ct/cast@1.0.0", func(d *resource.Document) {
+ d.Schema = `{"type":"object","required":["role"]}`
+ })
+ obj := doc("context", "t/notes/n@1.0.0", func(d *resource.Document) {
+ d.ContextType = "t/ct/cast@1.0.0"
+ d.ContextFields = map[string]resource.FieldValue{"role": {Kind: "scalar", Scalar: "owner"}}
+ })
+ if _, err := New(docSet(ct, obj)).ResolveAll(); err != nil {
+ t.Fatalf("a present required field should validate: %v", err)
+ }
+}
+
+func TestContextFieldTypeMismatchRejected(t *testing.T) {
+ ct := doc("contextType", "t/ct/cast@1.0.0", func(d *resource.Document) {
+ d.Schema = `{"type":"object","properties":{"tags":{"type":"array"}}}`
+ })
+ obj := doc("context", "t/notes/n@1.0.0", func(d *resource.Document) {
+ d.ContextType = "t/ct/cast@1.0.0"
+ d.ContextFields = map[string]resource.FieldValue{"tags": {Kind: "scalar", Scalar: "not-an-array"}}
+ })
+ _, err := New(docSet(ct, obj)).ResolveAll()
+ if err == nil || !strings.Contains(err.Error(), "must be array") {
+ t.Fatalf("a scalar in an array-typed field must be rejected, got %v", err)
+ }
+}
+
+func TestContextFieldTypeMatchAccepted(t *testing.T) {
+ ct := doc("contextType", "t/ct/cast@1.0.0", func(d *resource.Document) {
+ d.Schema = `{"type":"object","properties":{"tags":{"type":"array"},"owner":{"type":"string"}}}`
+ })
+ obj := doc("context", "t/notes/n@1.0.0", func(d *resource.Document) {
+ d.ContextType = "t/ct/cast@1.0.0"
+ d.ContextFields = map[string]resource.FieldValue{
+ "tags": {Kind: "sequence"},
+ "owner": {Kind: "scalar", Scalar: "Dana"},
+ }
+ })
+ if _, err := New(docSet(ct, obj)).ResolveAll(); err != nil {
+ t.Fatalf("matching field types should validate: %v", err)
+ }
+}
+
+func TestRequiredFieldFromRefinedTypeEnforced(t *testing.T) {
+ base := doc("contextType", "t/ct/cast@1.0.0", func(d *resource.Document) {
+ d.Schema = `{"type":"object","required":["role"]}`
+ })
+ gov := doc("contextType", "t/ct/gov@1.0.0", func(d *resource.Document) { d.Embeds = []string{"t/ct/cast@1.0.0"} })
+ obj := doc("context", "t/notes/n@1.0.0", func(d *resource.Document) { d.ContextType = "t/ct/gov@1.0.0" })
+ _, err := New(docSet(base, gov, obj)).ResolveAll()
+ if err == nil || !strings.Contains(err.Error(), "missing required field") {
+ t.Fatalf("a required field from a refined type must be enforced, got %v", err)
+ }
+}
+
+func TestSchemalessContextTypeImposesNoFields(t *testing.T) {
+ ct := doc("contextType", "t/ct/cast@1.0.0", nil) // no schema
+ obj := doc("context", "t/notes/n@1.0.0", func(d *resource.Document) { d.ContextType = "t/ct/cast@1.0.0" })
+ if _, err := New(docSet(ct, obj)).ResolveAll(); err != nil {
+ t.Fatalf("a schemaless contextType should impose no field requirements: %v", err)
+ }
+}
diff --git a/go/internal/resolve/sealing_test.go b/go/internal/resolve/sealing_test.go
new file mode 100644
index 0000000..c60577b
--- /dev/null
+++ b/go/internal/resolve/sealing_test.go
@@ -0,0 +1,78 @@
+package resolve
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/buchk/TypeFerence/go/internal/resource"
+)
+
+// sealedSetup: a deep profile that seals capability C with skill s1.
+func sealedSetup() map[string]*resource.Document {
+ capC := doc("capability", "t/cap/c@1.0.0", nil)
+ s1 := doc("skill", "t/skills/s1@1.0.0", func(d *resource.Document) { d.Binds = "t/cap/c@1.0.0" })
+ deep := doc("profile", "t/deep@1.0.0", func(d *resource.Document) {
+ d.Skills = []resource.SkillBinding{{Ref: "t/skills/s1@1.0.0", Sealed: true}}
+ })
+ return docSet(capC, s1, deep)
+}
+
+func TestSealedCapabilityCannotBeReboundLocally(t *testing.T) {
+ set := sealedSetup()
+ set["t/skills/s2@1.0.0"] = doc("skill", "t/skills/s2@1.0.0", func(d *resource.Document) { d.Binds = "t/cap/c@1.0.0" })
+ set["t/agent@1.0.0"] = doc("agent", "t/agent@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/deep@1.0.0"}
+ d.Skills = []resource.SkillBinding{{Ref: "t/skills/s2@1.0.0"}} // rebinds sealed C
+ })
+ _, err := New(set).Resolve("t/agent@1.0.0")
+ if err == nil || !strings.Contains(err.Error(), "sealed") {
+ t.Fatalf("expected sealed-rebind error, got %v", err)
+ }
+}
+
+func TestSealedCapabilityAllowsAdjacentExtension(t *testing.T) {
+ set := sealedSetup()
+ set["t/cap/d@1.0.0"] = doc("capability", "t/cap/d@1.0.0", nil)
+ set["t/skills/d@1.0.0"] = doc("skill", "t/skills/d@1.0.0", func(d *resource.Document) { d.Binds = "t/cap/d@1.0.0" })
+ set["t/agent@1.0.0"] = doc("agent", "t/agent@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/deep@1.0.0"}
+ d.Skills = []resource.SkillBinding{{Ref: "t/skills/d@1.0.0"}} // adjacent capability, not an override
+ })
+ resolved, err := New(set).Resolve("t/agent@1.0.0")
+ if err != nil {
+ t.Fatalf("adding an adjacent capability alongside a sealed one must be allowed: %v", err)
+ }
+ if len(resolved.Skills) != 2 {
+ t.Errorf("expected the sealed capability plus the added one, got %d skills", len(resolved.Skills))
+ }
+}
+
+func TestSealedCapabilityShallowerOverrideRejected(t *testing.T) {
+ // A mid profile embeds the sealing profile and tries to rebind C: rejected.
+ set := sealedSetup()
+ set["t/skills/s2@1.0.0"] = doc("skill", "t/skills/s2@1.0.0", func(d *resource.Document) { d.Binds = "t/cap/c@1.0.0" })
+ set["t/mid@1.0.0"] = doc("profile", "t/mid@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/deep@1.0.0"}
+ d.Skills = []resource.SkillBinding{{Ref: "t/skills/s2@1.0.0"}}
+ })
+ _, err := New(set).ResolveAll()
+ if err == nil || !strings.Contains(err.Error(), "sealed") {
+ t.Fatalf("expected sealed-override error at the mid profile, got %v", err)
+ }
+}
+
+func TestSealedCapabilityUntouchedResolves(t *testing.T) {
+ // Embedding a sealing profile without touching the sealed capability is fine;
+ // the sealed skill is carried through.
+ set := sealedSetup()
+ set["t/agent@1.0.0"] = doc("agent", "t/agent@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/deep@1.0.0"}
+ })
+ resolved, err := New(set).Resolve("t/agent@1.0.0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(resolved.Skills) != 1 || !resolved.Skills[0].Sealed {
+ t.Errorf("expected one carried sealed skill, got %+v", resolved.Skills)
+ }
+}
diff --git a/go/internal/resolve/typed_context_test.go b/go/internal/resolve/typed_context_test.go
new file mode 100644
index 0000000..6980ce6
--- /dev/null
+++ b/go/internal/resolve/typed_context_test.go
@@ -0,0 +1,169 @@
+package resolve
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/buchk/TypeFerence/go/internal/resource"
+)
+
+// baseSkillAgent returns a capability + skill + agent that resolve cleanly, so
+// tests can layer context/tool requirements onto the skill.
+func baseSkillAgent(mutateSkill, mutateAgent func(*resource.Document)) map[string]*resource.Document {
+ cap := doc("capability", "t/cap/c@1.0.0", nil)
+ skill := doc("skill", "t/skills/s@1.0.0", func(d *resource.Document) {
+ d.Binds = "t/cap/c@1.0.0"
+ if mutateSkill != nil {
+ mutateSkill(d)
+ }
+ })
+ agent := doc("agent", "t/agent@1.0.0", func(d *resource.Document) {
+ d.Skills = []resource.SkillBinding{{Ref: "t/skills/s@1.0.0"}}
+ if mutateAgent != nil {
+ mutateAgent(d)
+ }
+ })
+ return docSet(cap, skill, agent)
+}
+
+func TestRequiresContextTypeSatisfiedByRefinement(t *testing.T) {
+ set := baseSkillAgent(
+ func(s *resource.Document) { s.RequiresContextTypes = []string{"t/ct/cast@1.0.0"} },
+ func(a *resource.Document) { a.Context = []string{"t/notes/roster@1.0.0"} },
+ )
+ set["t/ct/cast@1.0.0"] = doc("contextType", "t/ct/cast@1.0.0", nil)
+ set["t/ct/governed@1.0.0"] = doc("contextType", "t/ct/governed@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/ct/cast@1.0.0"} // governed refines cast
+ })
+ set["t/notes/roster@1.0.0"] = doc("context", "t/notes/roster@1.0.0", func(d *resource.Document) {
+ d.ContextType = "t/ct/governed@1.0.0" // holds the refinement
+ })
+ if _, err := New(set).Resolve("t/agent@1.0.0"); err != nil {
+ t.Fatalf("a held refinement should satisfy a base-type requirement: %v", err)
+ }
+}
+
+func TestRequiresContextTypeUnsatisfied(t *testing.T) {
+ set := baseSkillAgent(
+ func(s *resource.Document) { s.RequiresContextTypes = []string{"t/ct/cast@1.0.0"} },
+ nil, // agent holds no context
+ )
+ set["t/ct/cast@1.0.0"] = doc("contextType", "t/ct/cast@1.0.0", nil)
+ _, err := New(set).Resolve("t/agent@1.0.0")
+ if err == nil || !strings.Contains(err.Error(), "requires context type") {
+ t.Fatalf("expected unsatisfied context-type error, got %v", err)
+ }
+}
+
+func TestContextRequirementIsAgentLevelNotProfileLevel(t *testing.T) {
+ // A profile binding a context-requiring skill must not error on its own; the
+ // requirement is checked once the agent supplies the context.
+ cap := doc("capability", "t/cap/c@1.0.0", nil)
+ skill := doc("skill", "t/skills/s@1.0.0", func(d *resource.Document) {
+ d.Binds = "t/cap/c@1.0.0"
+ d.RequiresContextTypes = []string{"t/ct/cast@1.0.0"}
+ })
+ profile := doc("profile", "t/profile@1.0.0", func(d *resource.Document) {
+ d.Skills = []resource.SkillBinding{{Ref: "t/skills/s@1.0.0"}}
+ })
+ ct := doc("contextType", "t/ct/cast@1.0.0", nil)
+ obj := doc("context", "t/notes/n@1.0.0", func(d *resource.Document) { d.ContextType = "t/ct/cast@1.0.0" })
+ agent := doc("agent", "t/agent@1.0.0", func(d *resource.Document) {
+ d.Embeds = []string{"t/profile@1.0.0"}
+ d.Context = []string{"t/notes/n@1.0.0"}
+ })
+ if _, err := New(docSet(cap, skill, profile, ct, obj, agent)).ResolveAll(); err != nil {
+ t.Fatalf("agent that supplies context should resolve the promoted requirement: %v", err)
+ }
+}
+
+func TestRequiresToolDeclared(t *testing.T) {
+ set := baseSkillAgent(
+ func(s *resource.Document) { s.RequiresTools = []string{"t/tools/reader@1.0.0"} },
+ nil,
+ )
+ set["t/tools/reader@1.0.0"] = doc("tool", "t/tools/reader@1.0.0", nil)
+ if _, err := New(set).Resolve("t/agent@1.0.0"); err != nil {
+ t.Fatalf("a declared tool should satisfy requiresTools: %v", err)
+ }
+}
+
+func TestRequiresToolUndeclared(t *testing.T) {
+ set := baseSkillAgent(
+ func(s *resource.Document) { s.RequiresTools = []string{"t/tools/missing@1.0.0"} },
+ nil,
+ )
+ _, err := New(set).Resolve("t/agent@1.0.0")
+ if err == nil || !strings.Contains(err.Error(), "requires tool") {
+ t.Fatalf("expected undeclared-tool error, got %v", err)
+ }
+}
+
+func TestContextTypeRefinementCycleRejected(t *testing.T) {
+ a := doc("contextType", "t/ct/a@1.0.0", func(d *resource.Document) { d.Embeds = []string{"t/ct/b@1.0.0"} })
+ b := doc("contextType", "t/ct/b@1.0.0", func(d *resource.Document) { d.Embeds = []string{"t/ct/a@1.0.0"} })
+ _, err := New(docSet(a, b)).ResolveAll()
+ if err == nil || !strings.Contains(err.Error(), "refinement cycle") {
+ t.Fatalf("expected refinement cycle error, got %v", err)
+ }
+}
+
+func TestToolInvalidSchemaRejected(t *testing.T) {
+ tool := doc("tool", "t/tools/reader@1.0.0", func(d *resource.Document) { d.InputSchema = "not json" })
+ _, err := New(docSet(tool)).ResolveAll()
+ if err == nil || !strings.Contains(err.Error(), "tool inputSchema") {
+ t.Fatalf("expected invalid tool schema error, got %v", err)
+ }
+}
+
+func TestNamedContextValuesMaterializeDefaultsAndPreserveScalarTypes(t *testing.T) {
+ nested := doc("contextType", "t/ct/nested@1.0.0", func(d *resource.Document) {
+ d.SchemaVersion = 4
+ d.ContextTypeFields = map[string]resource.ContextField{
+ "label": {Type: resource.TypeExpr{Kind: "string"}, Required: true},
+ "enabled": {
+ Type: resource.TypeExpr{Kind: "boolean"},
+ HasDefault: true,
+ Default: resource.FieldValue{Kind: "scalar", Scalar: "true"},
+ },
+ }
+ })
+ wrapper := doc("contextType", "t/ct/wrapper@1.0.0", func(d *resource.Document) {
+ d.SchemaVersion = 4
+ d.ContextTypeFields = map[string]resource.ContextField{
+ "nested": {Type: resource.TypeExpr{Kind: "ref", Ref: nested.ID}, Required: true},
+ }
+ })
+ value := doc("context", "t/context/value@1.0.0", func(d *resource.Document) {
+ d.SchemaVersion = 4
+ d.ContextType = wrapper.ID
+ d.ContextFields = map[string]resource.FieldValue{
+ "nested": {Kind: "map", Map: map[string]resource.FieldValue{
+ "label": {Kind: "scalar", Scalar: "ready"},
+ }},
+ }
+ })
+ agent := doc("agent", "t/agent@1.0.0", func(d *resource.Document) {
+ d.SchemaVersion = 4
+ d.Context = []string{value.ID}
+ })
+ resolved, err := New(docSet(nested, wrapper, value, agent)).ResolveAll()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got := resolved[0].ContextObjects[0].ValuesJSON; got != `{"nested":{"enabled":true,"label":"ready"}}` {
+ t.Fatalf("named value was not materialized with typed defaults: %s", got)
+ }
+}
+
+func TestNamedContextValueCyclesRejected(t *testing.T) {
+ cyclic := doc("contextType", "t/ct/cyclic@1.0.0", func(d *resource.Document) {
+ d.SchemaVersion = 4
+ d.ContextTypeFields = map[string]resource.ContextField{
+ "next": {Type: resource.TypeExpr{Kind: "ref", Ref: "t/ct/cyclic@1.0.0"}},
+ }
+ })
+ if _, err := New(docSet(cyclic)).ResolveAll(); err == nil || !strings.Contains(err.Error(), "value type cycle") {
+ t.Fatalf("expected recursive named value type diagnostic, got %v", err)
+ }
+}
diff --git a/go/internal/resolve/variants_test.go b/go/internal/resolve/variants_test.go
new file mode 100644
index 0000000..b472129
--- /dev/null
+++ b/go/internal/resolve/variants_test.go
@@ -0,0 +1,75 @@
+package resolve
+
+import (
+ "testing"
+
+ "github.com/buchk/TypeFerence/go/internal/resource"
+)
+
+func TestVariantsResolveWithPreferredDefault(t *testing.T) {
+ set := baseSkillAgent(func(s *resource.Document) {
+ s.Variants = map[string]resource.Variant{
+ "pipeline": {Instructions: "strict json"},
+ "manual": {Instructions: "explain"},
+ "a2a": {Instructions: "attributed"},
+ }
+ }, nil)
+ agent, err := New(set).Resolve("t/agent@1.0.0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ skill := agent.Skills[0]
+ if skill.Instructions != "strict json" {
+ t.Errorf("default rendering should prefer pipeline, got %q", skill.Instructions)
+ }
+ if len(skill.Variants) != 3 || skill.Variants["manual"] != "explain" || skill.Variants["a2a"] != "attributed" {
+ t.Errorf("variants not carried through resolution: %+v", skill.Variants)
+ }
+}
+
+func TestVariantsDefaultFallsBackAlphabetically(t *testing.T) {
+ set := baseSkillAgent(func(s *resource.Document) {
+ s.Variants = map[string]resource.Variant{
+ "beta": {Instructions: "b"},
+ "alpha": {Instructions: "a"},
+ }
+ }, nil)
+ agent, err := New(set).Resolve("t/agent@1.0.0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if agent.Skills[0].Instructions != "a" {
+ t.Errorf("with no preferred mode, default should be alphabetically-first 'alpha', got %q", agent.Skills[0].Instructions)
+ }
+}
+
+func TestInstructionsForSelectsVariantOrDefault(t *testing.T) {
+ s := ResolvedSkill{
+ Instructions: "default",
+ Variants: map[string]string{"a2a": "attributed", "manual": "explain"},
+ }
+ if got := s.InstructionsFor("a2a"); got != "attributed" {
+ t.Errorf("InstructionsFor(a2a) = %q, want attributed", got)
+ }
+ if got := s.InstructionsFor("nope"); got != "default" {
+ t.Errorf("InstructionsFor(missing mode) should fall back to default, got %q", got)
+ }
+ uni := ResolvedSkill{Instructions: "only"}
+ if got := uni.InstructionsFor("a2a"); got != "only" {
+ t.Errorf("unimodal InstructionsFor should return Instructions, got %q", got)
+ }
+}
+
+func TestUnimodalSkillHasNoVariants(t *testing.T) {
+ set := baseSkillAgent(func(s *resource.Document) { s.Instructions = "just do it" }, nil)
+ agent, err := New(set).Resolve("t/agent@1.0.0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if agent.Skills[0].Variants != nil {
+ t.Errorf("unimodal skill should carry no variants map")
+ }
+ if agent.Skills[0].Instructions != "just do it" {
+ t.Errorf("unimodal instructions changed: %q", agent.Skills[0].Instructions)
+ }
+}
diff --git a/go/internal/resource/loader.go b/go/internal/resource/loader.go
index 9b819bf..63ab33a 100644
--- a/go/internal/resource/loader.go
+++ b/go/internal/resource/loader.go
@@ -26,10 +26,20 @@ var slotName = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._-]*$`)
// IsResourceID reports whether id is a well-formed resource identifier.
func IsResourceID(id string) bool { return resourceID.MatchString(id) }
-// Load reads every *.yaml resource beneath sourceDir (excluding the trust
-// configuration), validates document shape, and returns documents keyed by
-// resource id.
+type LoadOptions struct {
+ // AllowLegacyV3 exists only so the historical conformance corpus can remain
+ // reproducible. Product entrypoints never enable it.
+ AllowLegacyV3 bool
+}
+
+// Load reads and validates the closed current source language.
func Load(sourceDir string, trustConfigPath string) (map[string]*Document, error) {
+ return LoadWithOptions(sourceDir, trustConfigPath, LoadOptions{})
+}
+
+// LoadWithOptions additionally supports the explicitly selected historical v3
+// grammar for conformance tooling.
+func LoadWithOptions(sourceDir string, trustConfigPath string, options LoadOptions) (map[string]*Document, error) {
root, err := filepath.Abs(sourceDir)
if err != nil {
return nil, Errorf("Source directory not found: %s", sourceDir)
@@ -37,7 +47,10 @@ func Load(sourceDir string, trustConfigPath string) (map[string]*Document, error
if info, statErr := os.Stat(root); statErr != nil || !info.IsDir() {
return nil, Errorf("Source directory not found: %s", root)
}
- excluded := []string{filepath.Join(root, "typeference.trust.yaml")}
+ excluded := []string{
+ filepath.Join(root, "typeference.trust.yaml"),
+ filepath.Join(root, ProjectManifestFile),
+ }
if trustConfigPath != "" {
if full, absErr := filepath.Abs(trustConfigPath); absErr == nil {
excluded = append(excluded, full)
@@ -48,7 +61,14 @@ func Load(sourceDir string, trustConfigPath string) (map[string]*Document, error
if err != nil {
return err
}
- if d.IsDir() || !strings.HasSuffix(d.Name(), ".yaml") {
+ name := d.Name()
+ if d.IsDir() {
+ if filepath.Dir(path) == root && (name == ".git" || name == "dist" || name == "bin" || name == "obj") {
+ return filepath.SkipDir
+ }
+ return nil
+ }
+ if !strings.HasSuffix(name, ".yaml") && !strings.HasSuffix(name, ".tfer") {
return nil
}
for _, ex := range excluded {
@@ -70,14 +90,34 @@ func Load(sourceDir string, trustConfigPath string) (map[string]*Document, error
if readErr != nil {
return nil, Errorf("%s: %s", file, readErr)
}
- doc, parseErr := parseDocument(stripBOM(string(raw)))
+ // Normalize CRLF to LF before parsing. A .tfer body is carried verbatim
+ // into artifacts, so without this the compiled output would depend on the
+ // checkout's line-ending configuration rather than on the source — the
+ // determinism contract is byte-identical output for identical source
+ // (ADR-0014). Mirrors compile.readTextFile.
+ text := stripBOM(strings.ReplaceAll(string(raw), "\r\n", "\n"))
+ isTfer := strings.HasSuffix(file, ".tfer")
+ var body string
+ if isTfer {
+ frontmatter, tferBody, splitErr := splitFrontmatter(text)
+ if splitErr != nil {
+ return nil, Errorf("%s: %s", file, splitErr)
+ }
+ text, body = frontmatter, tferBody
+ }
+ doc, parseErr := parseDocument(text)
if parseErr != nil {
return nil, Errorf("%s: invalid YAML resource: %s", file, parseErr)
}
if doc == nil {
return nil, Errorf("Empty resource: %s", file)
}
- if err := validateShape(doc, file, root); err != nil {
+ if isTfer {
+ if err := applyBody(doc, body, file); err != nil {
+ return nil, err
+ }
+ }
+ if err := validateShape(doc, file, root, options.AllowLegacyV3); err != nil {
return nil, err
}
if _, exists := result[doc.ID]; exists {
@@ -121,6 +161,18 @@ func parseDocument(text string) (*Document, error) {
return nil, Errorf("expected a single YAML document")
}
doc := NewDocument()
+ // Version 3 context objects carried values as extra frontmatter fields.
+ // Version 4 closes the shape and requires the explicit `values` mapping.
+ kind := scanKind(&node)
+ schemaVersion := scanSchemaVersion(&node)
+ extraFields := map[string]*yaml.Node{}
+ unknown := func(key string, value *yaml.Node) error {
+ if kind == "context" && schemaVersion == 3 {
+ extraFields[key] = value
+ return nil
+ }
+ return Errorf("property '%s' not found", key)
+ }
if err := decodeMapping(&node, map[string]fieldDecoder{
"schemaVersion": intField(&doc.SchemaVersion),
"kind": stringField(&doc.Kind),
@@ -130,7 +182,7 @@ func parseDocument(text string) (*Document, error) {
"binds": stringField(&doc.Binds),
"emit": boolField(&doc.Emit),
"embeds": stringListField(&doc.Embeds),
- "requiresSlots": stringListField(&doc.RequiresSlots),
+ "requiresSlots": slotRequirementsField(doc),
"requiresCapabilities": stringListField(&doc.RequiresCapabilities),
"slots": stringMapField(&doc.Slots),
"workingNorms": stringListField(&doc.WorkingNorms),
@@ -139,12 +191,133 @@ func parseDocument(text string) (*Document, error) {
"instructions": stringField(&doc.Instructions),
"inputSchema": stringField(&doc.InputSchema),
"outputSchema": stringField(&doc.OutputSchema),
- }); err != nil {
+ "contextType": stringField(&doc.ContextType),
+ "schema": stringField(&doc.Schema),
+ "fields": contextTypeFieldsField(&doc.ContextTypeFields),
+ "body": contextBodyField(&doc.ContextBody),
+ "values": contextValuesField(&doc.ContextFields),
+ "requiresContextTypes": stringListField(&doc.RequiresContextTypes),
+ "context": stringListField(&doc.Context),
+ "requiresTools": stringListField(&doc.RequiresTools),
+ "visibility": stringField(&doc.Visibility),
+ "variants": variantsField(&doc.Variants),
+ "allowedContextTypes": func(n *yaml.Node) error {
+ doc.HasAllowedContextTypes = true
+ return stringListField(&doc.AllowedContextTypes)(n)
+ },
+ }, unknown); err != nil {
return nil, err
}
+ if len(extraFields) > 0 {
+ doc.ContextFields = map[string]FieldValue{}
+ for key, value := range extraFields {
+ switch value.Kind {
+ case yaml.SequenceNode:
+ doc.ContextFields[key] = FieldValue{Kind: "sequence"}
+ case yaml.MappingNode:
+ doc.ContextFields[key] = FieldValue{Kind: "mapping"}
+ default:
+ doc.ContextFields[key] = FieldValue{Kind: "scalar", Scalar: value.Value}
+ }
+ }
+ }
return doc, nil
}
+func scanSchemaVersion(node *yaml.Node) int {
+ n := resolveAlias(node)
+ if n.Kind == yaml.DocumentNode && len(n.Content) == 1 {
+ n = resolveAlias(n.Content[0])
+ }
+ if n.Kind != yaml.MappingNode {
+ return 0
+ }
+ for i := 0; i+1 < len(n.Content); i += 2 {
+ key := resolveAlias(n.Content[i])
+ value := resolveAlias(n.Content[i+1])
+ if key.Kind == yaml.ScalarNode && key.Value == "schemaVersion" && value.Kind == yaml.ScalarNode {
+ version, _ := strconv.Atoi(value.Value)
+ return version
+ }
+ }
+ return 0
+}
+
+// scanKind reads the top-level `kind` scalar without a full decode, so the
+// decoder can decide whether unknown frontmatter keys are context fields or an
+// error.
+func scanKind(node *yaml.Node) string {
+ n := resolveAlias(node)
+ if n.Kind == yaml.DocumentNode && len(n.Content) == 1 {
+ n = resolveAlias(n.Content[0])
+ }
+ if n.Kind != yaml.MappingNode {
+ return ""
+ }
+ for i := 0; i+1 < len(n.Content); i += 2 {
+ key := resolveAlias(n.Content[i])
+ if key.Kind == yaml.ScalarNode && key.Value == "kind" {
+ if v := resolveAlias(n.Content[i+1]); v.Kind == yaml.ScalarNode {
+ return v.Value
+ }
+ }
+ }
+ return ""
+}
+
+// splitFrontmatter separates a .tfer file into its YAML frontmatter and its
+// verbatim markdown body. The file MUST begin with a `---` fence line; the
+// frontmatter runs to the next `---` fence line and the body is everything
+// after it, preserved byte-for-byte (ADR-0013 format: typed head, prose tail).
+func splitFrontmatter(text string) (frontmatter, body string, err error) {
+ nl := strings.IndexByte(text, '\n')
+ if nl < 0 || strings.TrimRight(text[:nl], "\r") != "---" {
+ return "", "", Errorf("a .tfer file must begin with a '---' frontmatter fence")
+ }
+ rest := text[nl+1:]
+ for idx := 0; ; {
+ lineEnd := strings.IndexByte(rest[idx:], '\n')
+ if lineEnd < 0 {
+ if strings.TrimRight(rest[idx:], "\r") == "---" {
+ return rest[:idx], "", nil
+ }
+ return "", "", Errorf("a .tfer file is missing its closing '---' frontmatter fence")
+ }
+ line := rest[idx : idx+lineEnd]
+ next := idx + lineEnd + 1
+ if strings.TrimRight(line, "\r") == "---" {
+ return rest[:idx], rest[next:], nil
+ }
+ idx = next
+ }
+}
+
+// applyBody materializes a .tfer markdown body onto the resource: a skill's
+// instructions or a context object's content. Kinds without a body field
+// reject a non-empty body (ADR-0013 format; ADR-0012 for the multimodal note).
+func applyBody(doc *Document, body, file string) error {
+ switch doc.Kind {
+ case "skill":
+ if strings.TrimSpace(body) == "" {
+ return nil
+ }
+ if len(doc.Variants) != 0 {
+ return Errorf("%s: a multimodal skill has no single body; put instructions inside each variant", file)
+ }
+ if strings.TrimSpace(doc.Instructions) != "" {
+ return Errorf("%s: a .tfer skill sets instructions in both the body and frontmatter; use one", file)
+ }
+ doc.Instructions = body
+ case "context":
+ doc.Content = body
+ default:
+ if strings.TrimSpace(body) != "" {
+ return Errorf("%s: a %s resource has no body field; put content in frontmatter", file, doc.Kind)
+ }
+ }
+ return nil
+}
+
type fieldDecoder func(*yaml.Node) error
func resolveAlias(node *yaml.Node) *yaml.Node {
@@ -154,7 +327,9 @@ func resolveAlias(node *yaml.Node) *yaml.Node {
return node
}
-func decodeMapping(node *yaml.Node, fields map[string]fieldDecoder) error {
+// decodeMapping decodes a mapping node against a field table. Unknown keys go
+// to the optional unknown handler; when it is nil an unknown key is an error.
+func decodeMapping(node *yaml.Node, fields map[string]fieldDecoder, unknown func(string, *yaml.Node) error) error {
node = resolveAlias(node)
if node.Kind == yaml.DocumentNode && len(node.Content) == 1 {
node = resolveAlias(node.Content[0])
@@ -162,13 +337,24 @@ func decodeMapping(node *yaml.Node, fields map[string]fieldDecoder) error {
if node.Kind != yaml.MappingNode {
return Errorf("expected a mapping")
}
+ seen := map[string]bool{}
for i := 0; i+1 < len(node.Content); i += 2 {
key := resolveAlias(node.Content[i])
if key.Kind != yaml.ScalarNode {
return Errorf("mapping keys must be scalars")
}
+ if seen[key.Value] {
+ return Errorf("duplicate property '%s'", key.Value)
+ }
+ seen[key.Value] = true
decoder, known := fields[key.Value]
if !known {
+ if unknown != nil {
+ if err := unknown(key.Value, resolveAlias(node.Content[i+1])); err != nil {
+ return err
+ }
+ continue
+ }
return Errorf("property '%s' not found", key.Value)
}
if err := decoder(resolveAlias(node.Content[i+1])); err != nil {
@@ -251,11 +437,16 @@ func stringMapField(target *map[string]string) fieldDecoder {
return Errorf("expected a mapping")
}
m := map[string]string{}
+ seen := map[string]bool{}
for i := 0; i+1 < len(node.Content); i += 2 {
key := resolveAlias(node.Content[i])
if key.Kind != yaml.ScalarNode {
return Errorf("mapping keys must be scalars")
}
+ if seen[key.Value] {
+ return Errorf("duplicate mapping key '%s'", key.Value)
+ }
+ seen[key.Value] = true
var v string
if err := scalarString(resolveAlias(node.Content[i+1]), &v); err != nil {
return err
@@ -267,6 +458,231 @@ func stringMapField(target *map[string]string) fieldDecoder {
}
}
+func slotRequirementsField(doc *Document) fieldDecoder {
+ return func(node *yaml.Node) error {
+ if node.Kind == yaml.SequenceNode {
+ return stringListField(&doc.RequiresSlots)(node)
+ }
+ if node.Kind != yaml.MappingNode {
+ return Errorf("requiresSlots must be a mapping from slot name to contextType")
+ }
+ values := map[string]string{}
+ for i := 0; i+1 < len(node.Content); i += 2 {
+ key := resolveAlias(node.Content[i])
+ if key.Kind != yaml.ScalarNode || !slotName.MatchString(key.Value) {
+ return Errorf("required slot names must be ASCII identifiers")
+ }
+ if _, duplicate := values[key.Value]; duplicate {
+ return Errorf("duplicate required slot '%s'", key.Value)
+ }
+ var contextType string
+ if err := scalarString(resolveAlias(node.Content[i+1]), &contextType); err != nil {
+ return err
+ }
+ values[key.Value] = contextType
+ }
+ doc.RequiredSlotTypes = values
+ doc.RequiresSlots = make([]string, 0, len(values))
+ for key := range values {
+ doc.RequiresSlots = append(doc.RequiresSlots, key)
+ }
+ sort.Strings(doc.RequiresSlots)
+ return nil
+ }
+}
+
+func decodeFieldValue(node *yaml.Node) FieldValue {
+ node = resolveAlias(node)
+ switch node.Kind {
+ case yaml.SequenceNode:
+ items := make([]FieldValue, 0, len(node.Content))
+ for _, item := range node.Content {
+ items = append(items, decodeFieldValue(item))
+ }
+ return FieldValue{Kind: "list", List: items}
+ case yaml.MappingNode:
+ values := map[string]FieldValue{}
+ for i := 0; i+1 < len(node.Content); i += 2 {
+ values[resolveAlias(node.Content[i]).Value] = decodeFieldValue(node.Content[i+1])
+ }
+ return FieldValue{Kind: "map", Map: values}
+ default:
+ if node.Tag == "!!null" {
+ return FieldValue{Kind: "null"}
+ }
+ return FieldValue{Kind: "scalar", Scalar: node.Value}
+ }
+}
+
+func validateValueNode(node *yaml.Node) error {
+ node = resolveAlias(node)
+ switch node.Kind {
+ case yaml.SequenceNode:
+ for _, item := range node.Content {
+ if err := validateValueNode(item); err != nil {
+ return err
+ }
+ }
+ case yaml.MappingNode:
+ seen := map[string]bool{}
+ for i := 0; i+1 < len(node.Content); i += 2 {
+ key := resolveAlias(node.Content[i])
+ if key.Kind != yaml.ScalarNode {
+ return Errorf("context value mapping keys must be scalars")
+ }
+ if seen[key.Value] {
+ return Errorf("duplicate context value key '%s'", key.Value)
+ }
+ seen[key.Value] = true
+ if err := validateValueNode(node.Content[i+1]); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func contextValuesField(target *map[string]FieldValue) fieldDecoder {
+ return func(node *yaml.Node) error {
+ if node.Kind != yaml.MappingNode {
+ return Errorf("expected a mapping")
+ }
+ values := map[string]FieldValue{}
+ for i := 0; i+1 < len(node.Content); i += 2 {
+ key := resolveAlias(node.Content[i])
+ if key.Kind != yaml.ScalarNode || !slotName.MatchString(key.Value) {
+ return Errorf("context field names must be ASCII identifiers")
+ }
+ if _, duplicate := values[key.Value]; duplicate {
+ return Errorf("duplicate context field '%s'", key.Value)
+ }
+ if err := validateValueNode(node.Content[i+1]); err != nil {
+ return err
+ }
+ values[key.Value] = decodeFieldValue(node.Content[i+1])
+ }
+ *target = values
+ return nil
+ }
+}
+
+func typeExpr(node *yaml.Node) (TypeExpr, error) {
+ node = resolveAlias(node)
+ if node.Kind == yaml.ScalarNode {
+ switch node.Value {
+ case "string", "text", "boolean", "integer", "number":
+ return TypeExpr{Kind: node.Value}, nil
+ default:
+ if resourceID.MatchString(node.Value) {
+ return TypeExpr{Kind: "ref", Ref: node.Value}, nil
+ }
+ return TypeExpr{}, Errorf("unknown context type '%s'", node.Value)
+ }
+ }
+ if node.Kind != yaml.MappingNode || len(node.Content) != 2 {
+ return TypeExpr{}, Errorf("a context type is a scalar or one of {list: T}/{map: T}")
+ }
+ constructor := resolveAlias(node.Content[0])
+ if constructor.Kind != yaml.ScalarNode || (constructor.Value != "list" && constructor.Value != "map") {
+ return TypeExpr{}, Errorf("unknown context type constructor")
+ }
+ elem, err := typeExpr(node.Content[1])
+ if err != nil {
+ return TypeExpr{}, err
+ }
+ return TypeExpr{Kind: constructor.Value, Elem: &elem}, nil
+}
+
+func contextTypeFieldsField(target *map[string]ContextField) fieldDecoder {
+ return func(node *yaml.Node) error {
+ if node.Kind != yaml.MappingNode {
+ return Errorf("expected a mapping")
+ }
+ fields := map[string]ContextField{}
+ for i := 0; i+1 < len(node.Content); i += 2 {
+ key := resolveAlias(node.Content[i])
+ if key.Kind != yaml.ScalarNode || !slotName.MatchString(key.Value) {
+ return Errorf("context field names must be ASCII identifiers")
+ }
+ if _, duplicate := fields[key.Value]; duplicate {
+ return Errorf("duplicate context field declaration '%s'", key.Value)
+ }
+ var field ContextField
+ err := decodeMapping(resolveAlias(node.Content[i+1]), map[string]fieldDecoder{
+ "type": func(n *yaml.Node) error {
+ t, err := typeExpr(n)
+ field.Type = t
+ return err
+ },
+ "required": boolField(&field.Required),
+ "default": func(n *yaml.Node) error {
+ if err := validateValueNode(n); err != nil {
+ return err
+ }
+ field.HasDefault = true
+ field.Default = decodeFieldValue(n)
+ return nil
+ },
+ }, nil)
+ if err != nil {
+ return err
+ }
+ if field.Type.Kind == "" {
+ return Errorf("context field '%s' must declare type", key.Value)
+ }
+ fields[key.Value] = field
+ }
+ *target = fields
+ return nil
+ }
+}
+
+func contextBodyField(target **ContextBody) fieldDecoder {
+ return func(node *yaml.Node) error {
+ body := &ContextBody{}
+ if err := decodeMapping(node, map[string]fieldDecoder{
+ "type": stringField(&body.Type),
+ "required": boolField(&body.Required),
+ }, nil); err != nil {
+ return err
+ }
+ if body.Type != "text" {
+ return Errorf("context body type must be 'text'")
+ }
+ *target = body
+ return nil
+ }
+}
+
+func variantsField(target *map[string]Variant) fieldDecoder {
+ return func(node *yaml.Node) error {
+ if node.Kind != yaml.MappingNode {
+ return Errorf("expected a mapping")
+ }
+ m := map[string]Variant{}
+ for i := 0; i+1 < len(node.Content); i += 2 {
+ key := resolveAlias(node.Content[i])
+ if key.Kind != yaml.ScalarNode {
+ return Errorf("mapping keys must be scalars")
+ }
+ if _, duplicate := m[key.Value]; duplicate {
+ return Errorf("duplicate variant '%s'", key.Value)
+ }
+ var v Variant
+ if err := decodeMapping(resolveAlias(node.Content[i+1]), map[string]fieldDecoder{
+ "instructions": stringField(&v.Instructions),
+ "requiresContextTypes": stringListField(&v.RequiresContextTypes),
+ "requiresTools": stringListField(&v.RequiresTools),
+ }, nil); err != nil {
+ return err
+ }
+ m[key.Value] = v
+ }
+ *target = m
+ return nil
+ }
+}
+
func skillsField(target *[]SkillBinding) fieldDecoder {
return func(node *yaml.Node) error {
if node.Kind != yaml.SequenceNode {
@@ -283,7 +699,9 @@ func skillsField(target *[]SkillBinding) fieldDecoder {
hasCapability = true
return scalarString(n, &capability)
},
- })
+ "sealed": boolField(&binding.Sealed),
+ "required": boolField(&binding.Required),
+ }, nil)
if err != nil {
return err
}
@@ -297,20 +715,177 @@ func skillsField(target *[]SkillBinding) fieldDecoder {
}
}
-func validateShape(doc *Document, file, root string) error {
- if doc.SchemaVersion != 3 {
- return Errorf("%s: schemaVersion must be 3", file)
+func validateShape(doc *Document, file, root string, allowLegacyV3 bool) error {
+ if err := validateDocumentShape(doc, file, allowLegacyV3); err != nil {
+ return err
+ }
+ var referenced []string
+ if doc.SchemaVersion == 3 {
+ referenced = append(referenced, doc.ContextFiles...)
+ for _, key := range SortedKeys(doc.Slots) {
+ referenced = append(referenced, doc.Slots[key])
+ }
+ }
+ for _, relative := range referenced {
+ full := filepath.Join(root, filepath.FromSlash(relative))
+ if !strings.HasPrefix(full, root+string(filepath.Separator)) {
+ return Errorf("%s: path escapes source root: %s", file, relative)
+ }
+ if info, err := os.Stat(full); err != nil || info.IsDir() {
+ return Errorf("%s: referenced file does not exist: %s", file, relative)
+ }
+ }
+ return nil
+}
+
+// validateDocumentShape validates a single resource in isolation: every rule
+// except the composition-level checks that require the whole source tree
+// (referenced-file existence). CheckDocument reuses it for the language server.
+func validateDocumentShape(doc *Document, file string, allowLegacyV3 bool) error {
+ if doc.SchemaVersion != 4 && !(allowLegacyV3 && doc.SchemaVersion == 3) {
+ return Errorf("%s: schemaVersion must be 4", file)
}
switch doc.Kind {
- case "agent", "profile", "interface", "capability", "skill":
+ case "agent", "profile", "interface", "capability", "skill", "context", "contextType", "tool":
default:
return Errorf("%s: unknown kind '%s'", file, doc.Kind)
}
if !resourceID.MatchString(doc.ID) {
return Errorf("%s: id must use lowercase namespace/name@semantic-version", file)
}
- if (doc.Kind == "capability" || doc.Kind == "skill") && len(doc.Embeds) != 0 {
- return Errorf("%s: %ss cannot embed resources", file, doc.Kind)
+ // capabilities, skills, context objects, and tools do not embed; contextTypes
+ // may embed to refine other contextTypes (ADR-0013), and agents/profiles/
+ // interfaces embed by design.
+ switch doc.Kind {
+ case "capability", "skill", "context", "tool":
+ if len(doc.Embeds) != 0 {
+ return Errorf("%s: %ss cannot embed resources", file, doc.Kind)
+ }
+ }
+ if doc.Kind == "context" {
+ if strings.TrimSpace(doc.ContextType) == "" {
+ return Errorf("%s: a context resource must declare a contextType", file)
+ }
+ if !resourceID.MatchString(doc.ContextType) {
+ return Errorf("%s: contextType must reference a contextType id", file)
+ }
+ } else if strings.TrimSpace(doc.ContextType) != "" {
+ return Errorf("%s: only context resources declare a contextType", file)
+ }
+ if doc.Kind == "contextType" {
+ if doc.SchemaVersion == 3 && strings.TrimSpace(doc.Schema) != "" {
+ if err := validateJSON(doc.Schema, file, "schema"); err != nil {
+ return err
+ }
+ } else if doc.SchemaVersion == 4 && strings.TrimSpace(doc.Schema) != "" {
+ return Errorf("%s: schemaVersion 4 contextTypes use native 'fields', not JSON Schema", file)
+ }
+ } else if strings.TrimSpace(doc.Schema) != "" {
+ return Errorf("%s: only contextType resources declare a schema", file)
+ }
+ if doc.SchemaVersion == 3 && (len(doc.ContextTypeFields) != 0 || doc.ContextBody != nil) {
+ return Errorf("%s: native context fields require schemaVersion 4", file)
+ }
+ if doc.Kind != "contextType" && (len(doc.ContextTypeFields) != 0 || doc.ContextBody != nil) {
+ return Errorf("%s: only contextType resources declare fields or body", file)
+ }
+ if doc.SchemaVersion == 4 && len(doc.ContextFiles) != 0 {
+ return Errorf("%s: contextFiles does not exist in schemaVersion 4; declare a typed context resource", file)
+ }
+ if doc.SchemaVersion == 4 {
+ if len(doc.RequiresSlots) > 0 && len(doc.RequiredSlotTypes) == 0 {
+ return Errorf("%s: schemaVersion 4 requiresSlots is a mapping from slot name to contextType id", file)
+ }
+ if len(doc.RequiredSlotTypes) > 0 && doc.Kind != "interface" {
+ return Errorf("%s: only interfaces declare typed slot requirements", file)
+ }
+ for name, contextType := range doc.RequiredSlotTypes {
+ if !resourceID.MatchString(contextType) {
+ return Errorf("%s: required slot '%s' must reference a contextType id", file, name)
+ }
+ }
+ }
+ if doc.SchemaVersion == 4 && doc.Kind != "context" && len(doc.ContextFields) != 0 {
+ return Errorf("%s: only context resources declare values", file)
+ }
+ if doc.SchemaVersion == 3 && doc.Kind == "context" && doc.ContextFields == nil {
+ doc.ContextFields = map[string]FieldValue{}
+ }
+ // requiresContextTypes / requiresTools are skill-only; context (holding by
+ // id) is agent/profile-only; visibility is capability-only.
+ if len(doc.RequiresContextTypes) != 0 && doc.Kind != "skill" {
+ return Errorf("%s: only skills declare requiresContextTypes", file)
+ }
+ if len(doc.RequiresTools) != 0 && doc.Kind != "skill" {
+ return Errorf("%s: only skills declare requiresTools", file)
+ }
+ if len(doc.Context) != 0 && doc.Kind != "agent" && doc.Kind != "profile" {
+ return Errorf("%s: only agents and profiles hold context by id", file)
+ }
+ if doc.HasAllowedContextTypes && doc.Kind != "agent" && doc.Kind != "profile" {
+ return Errorf("%s: only agents and profiles declare allowedContextTypes", file)
+ }
+ // A binding names its implementation with "ref". Omitting "ref" declares an
+ // abstract requirement instead: a capability an embedder must bind. That is
+ // meaningful only when mandated, so it must be required (ADR-0016).
+ for _, binding := range doc.Skills {
+ if strings.TrimSpace(binding.Ref) != "" {
+ continue
+ }
+ if binding.Capability == nil || strings.TrimSpace(*binding.Capability) == "" {
+ return Errorf("%s: a skill binding must set 'ref', or name a 'capability' to declare an abstract requirement", file)
+ }
+ if !binding.Required {
+ return Errorf("%s: abstract requirement on capability '%s' must set 'required: true'; an unbound optional capability has no effect", file, *binding.Capability)
+ }
+ if binding.Sealed {
+ return Errorf("%s: abstract requirement on capability '%s' cannot be sealed; sealing protects a concrete implementation", file, *binding.Capability)
+ }
+ if !resourceID.MatchString(*binding.Capability) {
+ return Errorf("%s: reference '%s' must be a namespace/name@semantic-version id", file, *binding.Capability)
+ }
+ }
+ refs := append([]string{}, doc.RequiresContextTypes...)
+ refs = append(refs, doc.RequiresTools...)
+ refs = append(refs, doc.Context...)
+ refs = append(refs, doc.AllowedContextTypes...)
+ for _, name := range doc.RequiresSlots {
+ if contextType := doc.RequiredSlotTypes[name]; contextType != "" {
+ refs = append(refs, contextType)
+ }
+ }
+ for _, v := range doc.Variants {
+ refs = append(refs, v.RequiresContextTypes...)
+ refs = append(refs, v.RequiresTools...)
+ }
+ for _, ref := range refs {
+ if !resourceID.MatchString(ref) {
+ return Errorf("%s: reference '%s' must be a namespace/name@semantic-version id", file, ref)
+ }
+ }
+ if strings.TrimSpace(doc.Visibility) != "" {
+ if doc.Kind != "capability" {
+ return Errorf("%s: only capabilities declare visibility", file)
+ }
+ if doc.Visibility != "internal" && doc.Visibility != "exposed" {
+ return Errorf("%s: visibility must be 'internal' or 'exposed'", file)
+ }
+ }
+ if len(doc.Variants) != 0 {
+ if doc.Kind != "skill" {
+ return Errorf("%s: only skills declare variants", file)
+ }
+ if strings.TrimSpace(doc.Instructions) != "" {
+ return Errorf("%s: a skill declares either instructions or variants, not both", file)
+ }
+ for mode, v := range doc.Variants {
+ if !slotName.MatchString(mode) {
+ return Errorf("%s: variant mode '%s' must be an ASCII identifier", file, mode)
+ }
+ if strings.TrimSpace(v.Instructions) == "" {
+ return Errorf("%s: variant '%s' must set instructions", file, mode)
+ }
+ }
}
if doc.Kind == "skill" && strings.TrimSpace(doc.Binds) == "" {
return Errorf("%s: skills must bind a capability", file)
@@ -327,18 +902,11 @@ func validateShape(doc *Document, file, root string) error {
return Errorf("%s: slot name '%s' must be an ASCII identifier matching [A-Za-z0-9][A-Za-z0-9._-]*", file, name)
}
}
- var referenced []string
- referenced = append(referenced, doc.ContextFiles...)
- for _, key := range SortedKeys(doc.Slots) {
- referenced = append(referenced, doc.Slots[key])
- }
- for _, relative := range referenced {
- full := filepath.Join(root, filepath.FromSlash(relative))
- if !strings.HasPrefix(full, root+string(filepath.Separator)) {
- return Errorf("%s: path escapes source root: %s", file, relative)
- }
- if info, err := os.Stat(full); err != nil || info.IsDir() {
- return Errorf("%s: referenced file does not exist: %s", file, relative)
+ if doc.SchemaVersion == 4 {
+ for _, name := range SortedKeys(doc.Slots) {
+ if !resourceID.MatchString(doc.Slots[name]) {
+ return Errorf("%s: schemaVersion 4 slot '%s' must reference a typed context resource id", file, name)
+ }
}
}
if err := validateJSON(doc.InputSchema, file, "inputSchema"); err != nil {
@@ -347,6 +915,35 @@ func validateShape(doc *Document, file, root string) error {
return validateJSON(doc.OutputSchema, file, "outputSchema")
}
+// CheckDocument parses one resource from its path and content and validates its
+// shape in isolation, without composition-level checks that need the whole
+// source tree. It is the diagnostic entrypoint used by the language server.
+func CheckDocument(path, content string) error {
+ text := stripBOM(content)
+ isTfer := strings.HasSuffix(path, ".tfer")
+ var body string
+ if isTfer {
+ frontmatter, tferBody, err := splitFrontmatter(text)
+ if err != nil {
+ return err
+ }
+ text, body = frontmatter, tferBody
+ }
+ doc, err := parseDocument(text)
+ if err != nil {
+ return Errorf("invalid YAML resource: %s", err)
+ }
+ if doc == nil {
+ return Errorf("empty resource")
+ }
+ if isTfer {
+ if err := applyBody(doc, body, filepath.Base(path)); err != nil {
+ return err
+ }
+ }
+ return validateDocumentShape(doc, filepath.Base(path), false)
+}
+
func validateJSON(value, file, field string) error {
if _, err := jsonx.Parse(value); err != nil {
return Errorf("%s: invalid %s: %s", file, field, err)
diff --git a/go/internal/resource/loader_test.go b/go/internal/resource/loader_test.go
index d9c4f26..837eeb3 100644
--- a/go/internal/resource/loader_test.go
+++ b/go/internal/resource/loader_test.go
@@ -22,6 +22,10 @@ func writeSource(t *testing.T, files map[string]string) string {
return root
}
+func loadLegacyForTest(root, trust string) (map[string]*Document, error) {
+ return LoadWithOptions(root, trust, LoadOptions{AllowLegacyV3: true})
+}
+
const minimalAgent = `schemaVersion: 3
kind: agent
id: t/agent@1.0.0
@@ -31,7 +35,7 @@ description: A test agent.
func TestLoadMinimalAgent(t *testing.T) {
root := writeSource(t, map[string]string{"agent.yaml": minimalAgent})
- docs, err := Load(root, "")
+ docs, err := loadLegacyForTest(root, "")
if err != nil {
t.Fatal(err)
}
@@ -49,23 +53,47 @@ func TestLoadMinimalAgent(t *testing.T) {
func TestUnknownFieldRejected(t *testing.T) {
root := writeSource(t, map[string]string{"agent.yaml": minimalAgent + "extends: something\n"})
- _, err := Load(root, "")
+ _, err := loadLegacyForTest(root, "")
if err == nil || !strings.Contains(err.Error(), "'extends'") {
t.Fatalf("expected unknown field error, got %v", err)
}
}
+func TestDuplicatePropertiesAndNestedContextKeysRejected(t *testing.T) {
+ root := writeSource(t, map[string]string{
+ "agent.yaml": "schemaVersion: 4\nkind: agent\nid: t/agents/a@1.0.0\ndescription: first\ndescription: second\n",
+ })
+ if _, err := Load(root, ""); err == nil || !strings.Contains(err.Error(), "duplicate property 'description'") {
+ t.Fatalf("expected duplicate property diagnostic, got %v", err)
+ }
+
+ root = writeSource(t, map[string]string{
+ "type.yaml": "schemaVersion: 4\nkind: contextType\nid: t/context-types/c@1.0.0\nfields:\n data:\n type:\n map: string\n",
+ "value.yaml": "schemaVersion: 4\nkind: context\nid: t/context/c@1.0.0\ncontextType: t/context-types/c@1.0.0\nvalues:\n data:\n owner: first\n owner: second\n",
+ })
+ if _, err := Load(root, ""); err == nil || !strings.Contains(err.Error(), "duplicate context value key 'owner'") {
+ t.Fatalf("expected nested duplicate context value diagnostic, got %v", err)
+ }
+}
+
func TestSchemaVersionEnforced(t *testing.T) {
root := writeSource(t, map[string]string{"agent.yaml": strings.Replace(minimalAgent, "schemaVersion: 3", "schemaVersion: 2", 1)})
- _, err := Load(root, "")
- if err == nil || !strings.Contains(err.Error(), "schemaVersion must be 3") {
+ _, err := loadLegacyForTest(root, "")
+ if err == nil || !strings.Contains(err.Error(), "schemaVersion must be 4") {
t.Fatalf("expected schemaVersion error, got %v", err)
}
}
+func TestDefaultLoaderRejectsLegacyV3(t *testing.T) {
+ root := writeSource(t, map[string]string{"agent.yaml": minimalAgent})
+ if _, err := Load(root, ""); err == nil || !strings.Contains(err.Error(), "schemaVersion must be 4") {
+ t.Fatalf("the default source language must be closed to v4, got %v", err)
+ }
+}
+
func TestInvalidIDRejected(t *testing.T) {
root := writeSource(t, map[string]string{"agent.yaml": strings.Replace(minimalAgent, "t/agent@1.0.0", "Bad Id", 1)})
- _, err := Load(root, "")
+ _, err := loadLegacyForTest(root, "")
if err == nil || !strings.Contains(err.Error(), "lowercase namespace/name@semantic-version") {
t.Fatalf("expected id error, got %v", err)
}
@@ -73,7 +101,7 @@ func TestInvalidIDRejected(t *testing.T) {
func TestDuplicateIDRejected(t *testing.T) {
root := writeSource(t, map[string]string{"a.yaml": minimalAgent, "b.yaml": minimalAgent})
- _, err := Load(root, "")
+ _, err := loadLegacyForTest(root, "")
if err == nil || !strings.Contains(err.Error(), "Duplicate resource id") {
t.Fatalf("expected duplicate error, got %v", err)
}
@@ -83,7 +111,7 @@ func TestPathEscapeRejected(t *testing.T) {
root := writeSource(t, map[string]string{
"agent.yaml": minimalAgent + "contextFiles:\n - ../outside.md\n",
})
- _, err := Load(root, "")
+ _, err := loadLegacyForTest(root, "")
if err == nil || !strings.Contains(err.Error(), "escapes source root") {
t.Fatalf("expected escape error, got %v", err)
}
@@ -93,7 +121,7 @@ func TestMissingContextFileRejected(t *testing.T) {
root := writeSource(t, map[string]string{
"agent.yaml": minimalAgent + "contextFiles:\n - context/missing.md\n",
})
- _, err := Load(root, "")
+ _, err := loadLegacyForTest(root, "")
if err == nil || !strings.Contains(err.Error(), "does not exist") {
t.Fatalf("expected missing file error, got %v", err)
}
@@ -103,7 +131,7 @@ func TestSkillMustBindCapability(t *testing.T) {
root := writeSource(t, map[string]string{
"skill.yaml": "schemaVersion: 3\nkind: skill\nid: t/skills/s@1.0.0\n",
})
- _, err := Load(root, "")
+ _, err := loadLegacyForTest(root, "")
if err == nil || !strings.Contains(err.Error(), "skills must bind a capability") {
t.Fatalf("expected binds error, got %v", err)
}
@@ -113,7 +141,7 @@ func TestOnlySkillsBind(t *testing.T) {
root := writeSource(t, map[string]string{
"agent.yaml": minimalAgent + "binds: t/capabilities/c@1.0.0\n",
})
- _, err := Load(root, "")
+ _, err := loadLegacyForTest(root, "")
if err == nil || !strings.Contains(err.Error(), "only skills can bind capabilities") {
t.Fatalf("expected binds error, got %v", err)
}
@@ -123,7 +151,7 @@ func TestCapabilitiesCannotEmbed(t *testing.T) {
root := writeSource(t, map[string]string{
"cap.yaml": "schemaVersion: 3\nkind: capability\nid: t/capabilities/c@1.0.0\nembeds:\n - t/other@1.0.0\n",
})
- _, err := Load(root, "")
+ _, err := loadLegacyForTest(root, "")
if err == nil || !strings.Contains(err.Error(), "cannot embed resources") {
t.Fatalf("expected embed error, got %v", err)
}
@@ -133,7 +161,7 @@ func TestInvalidSchemaJSONRejected(t *testing.T) {
root := writeSource(t, map[string]string{
"agent.yaml": minimalAgent + "inputSchema: 'not json'\n",
})
- _, err := Load(root, "")
+ _, err := loadLegacyForTest(root, "")
if err == nil || !strings.Contains(err.Error(), "invalid inputSchema") {
t.Fatalf("expected schema error, got %v", err)
}
@@ -144,7 +172,7 @@ func TestTrustConfigurationExcluded(t *testing.T) {
"agent.yaml": minimalAgent,
"typeference.trust.yaml": "schemaVersion: 1\nsource:\n identity: https://example.com\n",
})
- docs, err := Load(root, "")
+ docs, err := loadLegacyForTest(root, "")
if err != nil {
t.Fatal(err)
}
@@ -155,7 +183,7 @@ func TestTrustConfigurationExcluded(t *testing.T) {
func TestEmptyResourceRejected(t *testing.T) {
root := writeSource(t, map[string]string{"empty.yaml": ""})
- _, err := Load(root, "")
+ _, err := loadLegacyForTest(root, "")
if err == nil || !strings.Contains(err.Error(), "Empty resource") {
t.Fatalf("expected empty resource error, got %v", err)
}
@@ -163,7 +191,7 @@ func TestEmptyResourceRejected(t *testing.T) {
func TestMultiDocumentRejected(t *testing.T) {
root := writeSource(t, map[string]string{"multi.yaml": minimalAgent + "---\n" + minimalAgent})
- _, err := Load(root, "")
+ _, err := loadLegacyForTest(root, "")
if err == nil || !strings.Contains(err.Error(), "single YAML document") {
t.Fatalf("expected multi-document error, got %v", err)
}
@@ -171,8 +199,190 @@ func TestMultiDocumentRejected(t *testing.T) {
func TestUnknownKindRejected(t *testing.T) {
root := writeSource(t, map[string]string{"agent.yaml": strings.Replace(minimalAgent, "kind: agent", "kind: widget", 1)})
- _, err := Load(root, "")
+ _, err := loadLegacyForTest(root, "")
if err == nil || !strings.Contains(err.Error(), "unknown kind") {
t.Fatalf("expected kind error, got %v", err)
}
}
+
+const tferSkill = `---
+schemaVersion: 3
+kind: skill
+id: t/skills/s@1.0.0
+binds: t/capabilities/c@1.0.0
+---
+Inspect the requested signals and report status, evidence, and risk.
+`
+
+func TestTferSkillBodyBecomesInstructions(t *testing.T) {
+ root := writeSource(t, map[string]string{"s.tfer": tferSkill})
+ docs, err := loadLegacyForTest(root, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ doc := docs["t/skills/s@1.0.0"]
+ if doc == nil {
+ t.Fatal("skill not loaded")
+ }
+ want := "Inspect the requested signals and report status, evidence, and risk.\n"
+ if doc.Instructions != want {
+ t.Errorf("body did not become instructions verbatim: got %q want %q", doc.Instructions, want)
+ }
+}
+
+func TestTferInstructionsInBothBodyAndFrontmatterRejected(t *testing.T) {
+ dual := strings.Replace(tferSkill, "binds: t/capabilities/c@1.0.0\n",
+ "binds: t/capabilities/c@1.0.0\ninstructions: from frontmatter\n", 1)
+ root := writeSource(t, map[string]string{"s.tfer": dual})
+ _, err := loadLegacyForTest(root, "")
+ if err == nil || !strings.Contains(err.Error(), "both the body and frontmatter") {
+ t.Fatalf("expected dual-instructions error, got %v", err)
+ }
+}
+
+func TestTferMissingOpeningFenceRejected(t *testing.T) {
+ root := writeSource(t, map[string]string{"s.tfer": "schemaVersion: 3\nkind: skill\nid: t/skills/s@1.0.0\nbinds: t/capabilities/c@1.0.0\n"})
+ _, err := loadLegacyForTest(root, "")
+ if err == nil || !strings.Contains(err.Error(), "must begin with a '---' frontmatter fence") {
+ t.Fatalf("expected opening-fence error, got %v", err)
+ }
+}
+
+func TestTferMissingClosingFenceRejected(t *testing.T) {
+ root := writeSource(t, map[string]string{"s.tfer": "---\nschemaVersion: 3\nkind: skill\nid: t/skills/s@1.0.0\nbinds: t/capabilities/c@1.0.0\n"})
+ _, err := loadLegacyForTest(root, "")
+ if err == nil || !strings.Contains(err.Error(), "missing its closing '---' frontmatter fence") {
+ t.Fatalf("expected closing-fence error, got %v", err)
+ }
+}
+
+func TestTferContextObjectLoads(t *testing.T) {
+ ctxType := `---
+schemaVersion: 3
+kind: contextType
+id: t/context-types/cast@1.0.0
+displayName: Cast of Characters
+schema: '{"type":"object","properties":{"role":{"type":"string"}}}'
+---
+`
+ ctxObj := `---
+schemaVersion: 3
+kind: context
+id: t/notes/principal@1.0.0
+contextType: t/context-types/cast@1.0.0
+---
+The principal prefers short decision briefs.
+`
+ root := writeSource(t, map[string]string{"cast.tfer": ctxType, "principal.tfer": ctxObj})
+ docs, err := loadLegacyForTest(root, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ ct := docs["t/context-types/cast@1.0.0"]
+ if ct == nil || ct.Kind != "contextType" {
+ t.Fatal("contextType not loaded")
+ }
+ obj := docs["t/notes/principal@1.0.0"]
+ if obj == nil {
+ t.Fatal("context object not loaded")
+ }
+ if obj.ContextType != "t/context-types/cast@1.0.0" {
+ t.Errorf("context object lost its contextType: %q", obj.ContextType)
+ }
+ if obj.Content != "The principal prefers short decision briefs.\n" {
+ t.Errorf("context body did not become content: %q", obj.Content)
+ }
+}
+
+func TestTferContextRequiresContextType(t *testing.T) {
+ root := writeSource(t, map[string]string{"n.tfer": "---\nschemaVersion: 3\nkind: context\nid: t/notes/n@1.0.0\n---\nbody\n"})
+ _, err := loadLegacyForTest(root, "")
+ if err == nil || !strings.Contains(err.Error(), "must declare a contextType") {
+ t.Fatalf("expected missing-contextType error, got %v", err)
+ }
+}
+
+func TestTferBodyOnBodylessKindRejected(t *testing.T) {
+ root := writeSource(t, map[string]string{"a.tfer": "---\nschemaVersion: 3\nkind: agent\nid: t/agent@1.0.0\n---\nstray body\n"})
+ _, err := loadLegacyForTest(root, "")
+ if err == nil || !strings.Contains(err.Error(), "has no body field") {
+ t.Fatalf("expected bodyless-kind error, got %v", err)
+ }
+}
+
+func TestContextObjectCollectsSchemaFields(t *testing.T) {
+ src := "schemaVersion: 3\nkind: context\nid: t/notes/n@1.0.0\ncontextType: t/ct/cast@1.0.0\nrole: owner\ntags:\n - a\n"
+ root := writeSource(t, map[string]string{"n.yaml": src})
+ docs, err := loadLegacyForTest(root, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ d := docs["t/notes/n@1.0.0"]
+ if d.ContextFields["role"].Scalar != "owner" || d.ContextFields["role"].Kind != "scalar" {
+ t.Errorf("scalar field 'role' not collected: %v", d.ContextFields)
+ }
+ if d.ContextFields["tags"].Kind != "sequence" {
+ t.Errorf("sequence field 'tags' should be recorded with sequence kind, got %v", d.ContextFields["tags"])
+ }
+}
+
+func TestNonContextStillRejectsUnknownFields(t *testing.T) {
+ // The context-field collection must not weaken strictness for other kinds.
+ root := writeSource(t, map[string]string{
+ "cap.yaml": "schemaVersion: 3\nkind: capability\nid: t/cap/c@1.0.0\nrole: owner\n",
+ })
+ _, err := loadLegacyForTest(root, "")
+ if err == nil || !strings.Contains(err.Error(), "'role'") {
+ t.Fatalf("a capability with an unknown field must still error, got %v", err)
+ }
+}
+
+func TestYamlAndTferInteroperate(t *testing.T) {
+ root := writeSource(t, map[string]string{
+ "agent.yaml": minimalAgent,
+ "skill.tfer": tferSkill,
+ })
+ docs, err := loadLegacyForTest(root, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if docs["t/agent@1.0.0"] == nil || docs["t/skills/s@1.0.0"] == nil {
+ t.Fatalf("expected both .yaml and .tfer resources, got %d", len(docs))
+ }
+}
+
+func TestBindingWithoutRefOrCapabilityRejected(t *testing.T) {
+ root := writeSource(t, map[string]string{
+ "agent.yaml": minimalAgent + "skills:\n - sealed: true\n",
+ })
+ _, err := loadLegacyForTest(root, "")
+ if err == nil || !strings.Contains(err.Error(), "must set 'ref'") {
+ t.Fatalf("expected binding shape error, got %v", err)
+ }
+}
+
+func TestAbstractRequirementMustBeRequired(t *testing.T) {
+ // A ref-less binding that mandates nothing is inert; reject it rather than
+ // silently accepting a declaration with no effect (ADR-0016).
+ root := writeSource(t, map[string]string{
+ "agent.yaml": minimalAgent + "skills:\n - capability: t/cap/c@1.0.0\n",
+ })
+ _, err := loadLegacyForTest(root, "")
+ if err == nil || !strings.Contains(err.Error(), "must set 'required: true'") {
+ t.Fatalf("expected inert-requirement error, got %v", err)
+ }
+}
+
+func TestAbstractRequirementAccepted(t *testing.T) {
+ root := writeSource(t, map[string]string{
+ "agent.yaml": minimalAgent + "skills:\n - capability: t/cap/c@1.0.0\n required: true\n",
+ })
+ docs, err := loadLegacyForTest(root, "")
+ if err != nil {
+ t.Fatalf("an abstract requirement is a valid binding: %v", err)
+ }
+ binding := docs["t/agent@1.0.0"].Skills[0]
+ if binding.Ref != "" || !binding.Required || *binding.Capability != "t/cap/c@1.0.0" {
+ t.Errorf("unexpected parsed binding: %+v", binding)
+ }
+}
diff --git a/go/internal/resource/model.go b/go/internal/resource/model.go
index 9d5411f..fc16633 100644
--- a/go/internal/resource/model.go
+++ b/go/internal/resource/model.go
@@ -1,5 +1,4 @@
-// Package resource loads and validates TypeFerence typed resources
-// (docs/specification.md, schemaVersion 3) from a source directory.
+// Package resource loads and validates TypeFerence typed resources.
package resource
import "fmt"
@@ -15,6 +14,7 @@ type Document struct {
Emit bool
Embeds []string
RequiresSlots []string
+ RequiredSlotTypes map[string]string
RequiresCapabilities []string
Slots map[string]string
WorkingNorms []string
@@ -23,22 +23,107 @@ type Document struct {
Instructions string
InputSchema string
OutputSchema string
+ // ContextType is the id of the contextType a `kind: context` object
+ // instantiates (ADR-0013).
+ ContextType string
+ // Schema is an optional JSON Schema over a `kind: contextType`'s
+ // frontmatter (ADR-0013).
+ Schema string
+ // Content is a `.tfer` markdown body materialized onto a resource: a
+ // skill's instructions or a context object's content (ADR-0013 format).
+ Content string
+ // ContextFields holds a `kind: context` object's schema-typed frontmatter
+ // fields (those beyond the standard keys). The declaring contextType's schema
+ // validates their presence and type (ADR-0013).
+ ContextFields map[string]FieldValue
+ // ContextTypeFields and ContextBody define the closed schemaVersion 4 native
+ // context language. Schema remains only for legacy schemaVersion 3 input.
+ ContextTypeFields map[string]ContextField
+ ContextBody *ContextBody
+ // RequiresContextTypes are contextType ids a skill needs; the holding
+ // agent must supply context satisfying each (ADR-0013).
+ RequiresContextTypes []string
+ // Context are context-object ids an agent or profile holds by reference
+ // rather than by path (ADR-0013 reference-by-id).
+ Context []string
+ // AllowedContextTypes whitelists the contextType ids a component (and
+ // anything embedding it) may hold; empty means unrestricted. Intersects
+ // through embeds — the most restrictive ancestor wins (ADR-0013).
+ AllowedContextTypes []string
+ // HasAllowedContextTypes distinguishes an omitted allow-list (unrestricted)
+ // from an explicit empty list (deny all).
+ HasAllowedContextTypes bool
+ // RequiresTools are tool ids a skill depends on; each must be declared and
+ // its interface shape-checked (ADR-0017).
+ RequiresTools []string
+ // Visibility is "internal" (default) or "exposed" for a capability
+ // (ADR-0015). Empty means internal.
+ Visibility string
+ // Variants holds mode-specific renderings for a multimodal skill
+ // (ADR-0012): mode name -> variant. A skill declares either Instructions
+ // or Variants, never both.
+ Variants map[string]Variant
}
// SkillBinding attaches a skill implementation (and optionally the capability
-// it must satisfy) to an agent or profile.
+// it must satisfy) to an agent or profile. Sealed marks the binding as
+// non-overridable by embedders; Required marks it mandatory (ADR-0016).
type SkillBinding struct {
Ref string
Capability *string
+ Sealed bool
+ Required bool
+}
+
+// FieldValue is a context object's frontmatter field: its structural Kind
+// ("scalar", "sequence", or "mapping") and, for scalars, the text value. Kind
+// lets the contextType schema check declared field types (ADR-0013).
+type FieldValue struct {
+ Kind string
+ Scalar string
+ List []FieldValue
+ Map map[string]FieldValue
+}
+
+// TypeExpr is one closed native context type expression.
+type TypeExpr struct {
+ Kind string
+ Elem *TypeExpr
+ Ref string
+}
+
+// ContextField declares one schemaVersion 4 context field.
+type ContextField struct {
+ Type TypeExpr
+ Required bool
+ HasDefault bool
+ Default FieldValue
+}
+
+// ContextBody declares the optional typed prose body of a contextType.
+type ContextBody struct {
+ Type string
+ Required bool
+}
+
+// Variant is a mode-specific rendering of a multimodal skill (ADR-0012). It
+// varies instructions and may narrow requirements; the capability contract
+// (schemas) is invariant.
+type Variant struct {
+ Instructions string
+ RequiresContextTypes []string
+ RequiresTools []string
}
// NewDocument returns a Document carrying the spec-defined defaults.
func NewDocument() *Document {
return &Document{
- Emit: true,
- Slots: map[string]string{},
- InputSchema: `{"type":"object","additionalProperties":false}`,
- OutputSchema: `{"type":"object"}`,
+ Emit: true,
+ Slots: map[string]string{},
+ RequiredSlotTypes: map[string]string{},
+ ContextTypeFields: map[string]ContextField{},
+ InputSchema: `{"type":"object","additionalProperties":false}`,
+ OutputSchema: `{"type":"object"}`,
}
}
diff --git a/go/internal/resource/project.go b/go/internal/resource/project.go
new file mode 100644
index 0000000..984c653
--- /dev/null
+++ b/go/internal/resource/project.go
@@ -0,0 +1,89 @@
+package resource
+
+import (
+ "bytes"
+ "os"
+ "path/filepath"
+ "regexp"
+ "sort"
+ "strings"
+
+ "gopkg.in/yaml.v3"
+)
+
+var packageName = regexp.MustCompile(`^[a-z0-9][a-z0-9.-]*(?:/[a-z0-9][a-z0-9.-]*)+$`)
+var semanticVersion = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$`)
+
+// Project is the optional schemaVersion 2 source-root manifest. It contains
+// stable source identity only; feeds and deployment are external.
+type Project struct {
+ Name string
+ Version string
+ Publisher string
+ Dependencies map[string]string
+}
+
+const ProjectManifestFile = "typeference.yaml"
+
+func LoadProject(sourceDir string) (*Project, error) {
+ raw, err := os.ReadFile(filepath.Join(sourceDir, ProjectManifestFile))
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil, nil
+ }
+ return nil, Errorf("%s: %s", ProjectManifestFile, err)
+ }
+ var doc struct {
+ SchemaVersion int `yaml:"schemaVersion"`
+ Name string `yaml:"name"`
+ Version string `yaml:"version"`
+ Publisher string `yaml:"publisher"`
+ Dependencies map[string]string `yaml:"dependencies"`
+ }
+ dec := yaml.NewDecoder(bytes.NewReader(raw))
+ dec.KnownFields(true)
+ if err := dec.Decode(&doc); err != nil {
+ return nil, Errorf("%s: invalid manifest: %s", ProjectManifestFile, err)
+ }
+ if doc.SchemaVersion != 2 {
+ return nil, Errorf("%s: schemaVersion must be 2", ProjectManifestFile)
+ }
+ if !packageName.MatchString(doc.Name) {
+ return nil, Errorf("%s: name must use a lowercase namespace/name", ProjectManifestFile)
+ }
+ if !semanticVersion.MatchString(doc.Version) {
+ return nil, Errorf("%s: version must be an exact semantic version", ProjectManifestFile)
+ }
+ if strings.TrimSpace(doc.Publisher) != doc.Publisher {
+ return nil, Errorf("%s: publisher must not contain surrounding whitespace", ProjectManifestFile)
+ }
+ dependencies := map[string]string{}
+ for name, version := range doc.Dependencies {
+ if !packageName.MatchString(name) {
+ return nil, Errorf("%s: dependency '%s' must use a lowercase namespace/name", ProjectManifestFile, name)
+ }
+ if !semanticVersion.MatchString(version) {
+ return nil, Errorf("%s: dependency '%s' must use an exact semantic version", ProjectManifestFile, name)
+ }
+ dependencies[name] = version
+ }
+ return &Project{
+ Name: doc.Name,
+ Version: doc.Version,
+ Publisher: doc.Publisher,
+ Dependencies: dependencies,
+ }, nil
+}
+
+// SortedDependencies returns dependency names in canonical order.
+func (p *Project) SortedDependencies() []string {
+ names := make([]string, 0, len(p.Dependencies))
+ for name := range p.Dependencies {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ return names
+}
+
+func IsPackageName(value string) bool { return packageName.MatchString(value) }
+func IsSemanticVersion(value string) bool { return semanticVersion.MatchString(value) }
diff --git a/go/internal/resource/variants_test.go b/go/internal/resource/variants_test.go
new file mode 100644
index 0000000..a013efe
--- /dev/null
+++ b/go/internal/resource/variants_test.go
@@ -0,0 +1,43 @@
+package resource
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestVariantsOnlyOnSkills(t *testing.T) {
+ root := writeSource(t, map[string]string{
+ "agent.yaml": minimalAgent + "variants:\n manual:\n instructions: hi\n",
+ })
+ _, err := loadLegacyForTest(root, "")
+ if err == nil || !strings.Contains(err.Error(), "only skills declare variants") {
+ t.Fatalf("expected variants-kind error, got %v", err)
+ }
+}
+
+func TestVariantsAndInstructionsMutuallyExclusive(t *testing.T) {
+ skill := "schemaVersion: 3\nkind: skill\nid: t/skills/s@1.0.0\nbinds: t/capabilities/c@1.0.0\ninstructions: top\nvariants:\n manual:\n instructions: hi\n"
+ root := writeSource(t, map[string]string{"s.yaml": skill})
+ _, err := loadLegacyForTest(root, "")
+ if err == nil || !strings.Contains(err.Error(), "either instructions or variants") {
+ t.Fatalf("expected instructions/variants exclusivity error, got %v", err)
+ }
+}
+
+func TestVariantRequiresInstructions(t *testing.T) {
+ skill := "schemaVersion: 3\nkind: skill\nid: t/skills/s@1.0.0\nbinds: t/capabilities/c@1.0.0\nvariants:\n manual:\n instructions: ''\n"
+ root := writeSource(t, map[string]string{"s.yaml": skill})
+ _, err := loadLegacyForTest(root, "")
+ if err == nil || !strings.Contains(err.Error(), "variant 'manual' must set instructions") {
+ t.Fatalf("expected empty-variant error, got %v", err)
+ }
+}
+
+func TestMultimodalTferSkillRejectsBody(t *testing.T) {
+ skill := "---\nschemaVersion: 3\nkind: skill\nid: t/skills/s@1.0.0\nbinds: t/capabilities/c@1.0.0\nvariants:\n manual:\n instructions: hi\n---\nstray body\n"
+ root := writeSource(t, map[string]string{"s.tfer": skill})
+ _, err := loadLegacyForTest(root, "")
+ if err == nil || !strings.Contains(err.Error(), "multimodal skill has no single body") {
+ t.Fatalf("expected multimodal-body error, got %v", err)
+ }
+}
diff --git a/output/pdf/typeference-whitepaper.pdf b/output/pdf/typeference-whitepaper.pdf
index cdfc96d..c8a339d 100644
Binary files a/output/pdf/typeference-whitepaper.pdf and b/output/pdf/typeference-whitepaper.pdf differ
diff --git a/src/TypeFerence.Cli/Program.cs b/src/TypeFerence.Cli/Program.cs
deleted file mode 100644
index 0090d9f..0000000
--- a/src/TypeFerence.Cli/Program.cs
+++ /dev/null
@@ -1,189 +0,0 @@
-using System.Text.Json;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using ModelContextProtocol.Server;
-using TypeFerence.Core;
-
-return await Entry.RunAsync(args);
-
-internal static class Entry
-{
- public static async Task RunAsync(string[] args)
- {
- try
- {
- if (args.Length == 0 || args[0] is "-h" or "--help" or "help") return Help();
- return args[0] switch
- {
- "validate" => Validate(args),
- "build" => Build(args),
- "inspect" => Inspect(args),
- "diff" => Diff(args),
- "serve" => await ServeAsync(args),
- "version" or "--version" => Version(),
- _ => Fail($"Unknown command: {args[0]}")
- };
- }
- catch (TypeFerenceException ex) { return Fail(ex.Message); }
- catch (Exception ex) { return Fail(ex.ToString()); }
- }
-
- private static int Validate(string[] args)
- {
- var source = Required(args, 1, "source");
- var agents = new TypeFerenceCompiler().Validate(source, Option(args, "--trust-config"));
- Console.WriteLine($"Valid: {agents.Count} agents resolved.");
- return 0;
- }
-
- private static int Build(string[] args)
- {
- var source = Required(args, 1, "source");
- var output = Option(args, "--out") ?? "dist";
- var target = Option(args, "--target") ?? "all";
- var publisherDomain = Option(args, "--publisher-domain");
- var trustConfig = Option(args, "--trust-config");
- var trustSignatures = Option(args, "--trust-signatures");
- var allowUnsignedTrust = args.Contains("--allow-unsigned-trust", StringComparer.Ordinal);
- var emitArd = args.Contains("--emit-ard", StringComparer.Ordinal);
- if (emitArd && publisherDomain is null) throw new TypeFerenceException("--emit-ard requires --publisher-domain");
- if (!emitArd && publisherDomain is not null) throw new TypeFerenceException("--publisher-domain requires --emit-ard");
- if (!emitArd && trustConfig is not null) throw new TypeFerenceException("--trust-config requires --emit-ard");
- if (!emitArd && trustSignatures is not null) throw new TypeFerenceException("--trust-signatures requires --emit-ard");
- if (!emitArd && allowUnsignedTrust) throw new TypeFerenceException("--allow-unsigned-trust requires --emit-ard");
- var files = new TypeFerenceCompiler().Build(
- source,
- output,
- ParseTargets(target),
- emitArd ? new ArdPublicationOptions(publisherDomain!, trustConfig, trustSignatures, allowUnsignedTrust) : null);
- Console.WriteLine($"Built {files.Count} files at {Path.GetFullPath(output)}");
- Console.WriteLine($"SHA-256 {TypeFerenceCompiler.HashDirectory(output)}");
- return 0;
- }
-
- private static int Inspect(string[] args)
- {
- var source = Option(args, "--source") ?? ".";
- var id = Required(args, 1, "agent id");
- var agent = new TypeFerenceCompiler().Validate(source).SingleOrDefault(x => x.Id == id)
- ?? throw new TypeFerenceException($"Agent not found: {id}");
- Console.WriteLine(JsonSerializer.Serialize(agent, new JsonSerializerOptions { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));
- return 0;
- }
-
- private static int Diff(string[] args)
- {
- var source = Required(args, 1, "source");
- var against = Option(args, "--against") ?? throw new TypeFerenceException("--against is required");
- var temp = Path.Combine(Path.GetTempPath(), "typeference-diff-" + Guid.NewGuid().ToString("N"));
- try
- {
- var publisherDomain = Option(args, "--publisher-domain");
- var trustConfig = Option(args, "--trust-config");
- var trustSignatures = Option(args, "--trust-signatures");
- var allowUnsignedTrust = args.Contains("--allow-unsigned-trust", StringComparer.Ordinal);
- var emitArd = args.Contains("--emit-ard", StringComparer.Ordinal);
- if (emitArd && publisherDomain is null) throw new TypeFerenceException("--emit-ard requires --publisher-domain");
- if (!emitArd && publisherDomain is not null) throw new TypeFerenceException("--publisher-domain requires --emit-ard");
- if (!emitArd && trustConfig is not null) throw new TypeFerenceException("--trust-config requires --emit-ard");
- if (!emitArd && trustSignatures is not null) throw new TypeFerenceException("--trust-signatures requires --emit-ard");
- if (!emitArd && allowUnsignedTrust) throw new TypeFerenceException("--allow-unsigned-trust requires --emit-ard");
- new TypeFerenceCompiler().Build(
- source,
- temp,
- ParseTargets(Option(args, "--target") ?? "all"),
- emitArd ? new ArdPublicationOptions(publisherDomain!, trustConfig, trustSignatures, allowUnsignedTrust) : null);
- var result = DiffResult.Compare(against, temp);
- if (args.Contains("--json", StringComparer.Ordinal)) Console.WriteLine(JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true }));
- else
- {
- foreach (var x in result.Added) Console.WriteLine($"+ {x}");
- foreach (var x in result.Removed) Console.WriteLine($"- {x}");
- foreach (var x in result.Changed) Console.WriteLine($"~ {x}");
- if (!result.Different) Console.WriteLine("No differences.");
- }
- return result.Different ? 1 : 0;
- }
- finally { if (Directory.Exists(temp)) Directory.Delete(temp, true); }
- }
-
- private static async Task ServeAsync(string[] args)
- {
- var source = Required(args, 1, "source or compiled directory");
- var agents = Directory.EnumerateFiles(source, "*.yaml", SearchOption.AllDirectories).Any()
- ? new TypeFerenceCompiler().Validate(source).Where(x => x.Emit).ToArray()
- : TypeFerenceCompiler.LoadCompiled(source).Where(x => x.Emit).ToArray();
- var tools = agents.SelectMany(agent => agent.Skills.Select(skill =>
- {
- Func handler = arguments => JsonSerializer.Serialize(TypeFerenceCompiler.Invoke(agent, skill, arguments));
- return McpServerTool.Create(handler, new McpServerToolCreateOptions
- {
- Name = skill.DispatchName,
- Description = skill.Description,
- ReadOnly = true,
- Destructive = false,
- OpenWorld = false
- });
- })).ToArray();
-
- var builder = Host.CreateApplicationBuilder();
- builder.Logging.AddConsole(options => options.LogToStandardErrorThreshold = LogLevel.Trace);
- builder.Services.AddMcpServer().WithStdioServerTransport().WithTools(tools);
- await builder.Build().RunAsync();
- return 0;
- }
-
- private static CompilationTarget[] ParseTargets(string target) => target.ToLowerInvariant() switch
- {
- "all" => Enum.GetValues(),
- "neutral" => [CompilationTarget.Neutral],
- "codex" => [CompilationTarget.Codex],
- "copilot" => [CompilationTarget.Copilot],
- "cursor" => [CompilationTarget.Cursor],
- _ => throw new TypeFerenceException($"Unknown target: {target}")
- };
-
- private static string Required(string[] args, int index, string name) => args.Length > index ? args[index] : throw new TypeFerenceException($"Missing {name}");
- private static string? Option(string[] args, string name)
- {
- var i = Array.IndexOf(args, name);
- if (i < 0) return null;
- if (i + 1 >= args.Length || args[i + 1].StartsWith("--", StringComparison.Ordinal))
- throw new TypeFerenceException($"{name} requires a value");
- return args[i + 1];
- }
- private static int Version()
- {
- var informational = typeof(Entry).Assembly
- .GetCustomAttributes(typeof(System.Reflection.AssemblyInformationalVersionAttribute), false)
- .OfType()
- .FirstOrDefault()?.InformationalVersion ?? "unknown";
- // Deterministic builds may append "+"; the version is the part before it.
- Console.WriteLine($"typeference {informational.Split('+')[0]}");
- return 0;
- }
-
- private static int Fail(string message) { Console.Error.WriteLine($"typeference: {message}"); return 2; }
- private static int Help()
- {
- Console.WriteLine("""
-TypeFerence - typed coherence for AI agents
-
-Commands:
- typeference validate [--trust-config path]
- typeference build [--target all|neutral|codex|copilot|cursor] [--out dist]
- [--emit-ard --publisher-domain example.com] [--trust-config path]
- [--trust-signatures signatures.json]
- [--allow-unsigned-trust]
- typeference inspect [--source path]
- typeference diff --against [--target all]
- [--emit-ard --publisher-domain example.com] [--trust-config path]
- [--trust-signatures signatures.json] [--json]
- [--allow-unsigned-trust]
- typeference serve
- typeference version
-""");
- return 0;
- }
-}
diff --git a/src/TypeFerence.Cli/TypeFerence.Cli.csproj b/src/TypeFerence.Cli/TypeFerence.Cli.csproj
deleted file mode 100644
index 20e1cbd..0000000
--- a/src/TypeFerence.Cli/TypeFerence.Cli.csproj
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
- Exe
- typeference
- TypeFerence compiler, diff tool, and MCP server.
-
-
-
-
-
-
-
diff --git a/src/TypeFerence.Core/CanonicalOrder.cs b/src/TypeFerence.Core/CanonicalOrder.cs
deleted file mode 100644
index a9f73db..0000000
--- a/src/TypeFerence.Core/CanonicalOrder.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System.Text;
-
-namespace TypeFerence.Core;
-
-///
-/// Canonical string ordering per the specification: lexicographic by Unicode
-/// code point (equivalently UTF-8 byte order). Differs from
-/// (UTF-16 code-unit order) for
-/// supplementary-plane code points, which UTF-16 orders below U+E000.
-///
-public sealed class CanonicalOrder : IComparer
-{
- public static readonly CanonicalOrder Instance = new();
-
- private CanonicalOrder()
- {
- }
-
- public int Compare(string? x, string? y)
- {
- if (ReferenceEquals(x, y)) return 0;
- if (x is null) return -1;
- if (y is null) return 1;
- var left = x.EnumerateRunes();
- var right = y.EnumerateRunes();
- while (true)
- {
- var hasLeft = left.MoveNext();
- var hasRight = right.MoveNext();
- if (!hasLeft || !hasRight) return hasLeft.CompareTo(hasRight);
- var compared = left.Current.Value.CompareTo(right.Current.Value);
- if (compared != 0) return compared;
- }
- }
-}
diff --git a/src/TypeFerence.Core/Compiler.cs b/src/TypeFerence.Core/Compiler.cs
deleted file mode 100644
index e255798..0000000
--- a/src/TypeFerence.Core/Compiler.cs
+++ /dev/null
@@ -1,495 +0,0 @@
-using System.Security.Cryptography;
-using System.Text;
-using System.Text.Json;
-
-namespace TypeFerence.Core;
-
-public enum CompilationTarget { Neutral, Codex, Copilot, Cursor }
-
-public sealed record ArdPublicationOptions(
- string PublisherDomain,
- string? TrustConfigurationPath = null,
- string? TrustSignaturesPath = null,
- bool AllowUnsignedTrust = false);
-
-public sealed class TypeFerenceCompiler
-{
- private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
-
- public IReadOnlyList Validate(string source, string? trustConfigurationPath = null)
- {
- var trust = TrustConfigurationLoader.Load(source, trustConfigurationPath);
- var resources = new ResourceLoader().Load(source, trust?.Path);
- return new TypeResolver(resources).ResolveAll();
- }
-
- public IReadOnlyList Build(
- string source,
- string output,
- IEnumerable targets,
- ArdPublicationOptions? ardPublication = null)
- {
- var trust = TrustConfigurationLoader.Load(source, ardPublication?.TrustConfigurationPath);
- var resources = new ResourceLoader().Load(source, trust?.Path);
- var agents = new TypeResolver(resources).ResolveAll().Where(x => x.Emit).OrderBy(x => x.Id, StringComparer.Ordinal).ToArray();
- var requestedTargets = targets.Distinct().Order().ToArray();
- if (requestedTargets.Length == 0) throw new TypeFerenceException("At least one compilation target is required");
- var root = Path.GetFullPath(output);
- Directory.CreateDirectory(root);
- var written = new List();
- foreach (var target in requestedTargets)
- {
- var targetRoot = Path.Combine(root, target.ToString().ToLowerInvariant());
- if (Directory.Exists(targetRoot)) Directory.Delete(targetRoot, true);
- Directory.CreateDirectory(targetRoot);
- foreach (var agent in agents) WriteTarget(target, targetRoot, agent, written);
- }
- if (ardPublication is not null)
- {
- var ardRoot = Path.Combine(root, "ard");
- if (Directory.Exists(ardRoot)) Directory.Delete(ardRoot, true);
- Directory.CreateDirectory(ardRoot);
- if (ardPublication.TrustSignaturesPath is not null)
- {
- var sourceRoot = Path.GetFullPath(source).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
- var signaturePath = Path.GetFullPath(ardPublication.TrustSignaturesPath);
- if (signaturePath.StartsWith(sourceRoot, StringComparison.OrdinalIgnoreCase))
- throw new TypeFerenceException("Trust signatures file must be outside the source root to avoid a digest/signature cycle");
- }
- var signatures = ardPublication.TrustSignaturesPath is null
- ? new SortedDictionary(StringComparer.Ordinal)
- : TrustConfigurationLoader.LoadSignatures(ardPublication.TrustSignaturesPath);
- if (signatures.Count > 0 && trust is null)
- throw new TypeFerenceException("--trust-signatures requires a trust configuration");
- if (ardPublication.AllowUnsignedTrust && trust is null)
- throw new TypeFerenceException("--allow-unsigned-trust requires a trust configuration");
- WriteArdCatalog(ardRoot, source, root, agents, requestedTargets, ardPublication.PublisherDomain,
- trust?.Configuration, signatures, ardPublication.AllowUnsignedTrust, written);
- }
- return written.Order(StringComparer.Ordinal).ToArray();
- }
-
- public static InvocationPackage Invoke(ResolvedAgent agent, ResolvedSkill skill, JsonElement arguments)
- {
- ValidateArguments(skill.InputSchema, arguments);
- return new(
- agent.Id,
- skill.ImplementationId,
- skill.DispatchName,
- JsonSerializer.Deserialize