Feat/type closing rewrite - #11
Open
buchk wants to merge 38 commits into
Open
Conversation
Formalizes the object model surfaced in the Obsidian-intersection design session: capabilities/skills/tools as contract/model-impl/code-impl, typed context as fields, and the governance primitives (exposure, sealing) that composition needs once it goes multiplayer. - 0012 (rev): mode variants gain per-variant requiresContextTypes; note the frontmatter-body vs multimodal cross-constraint - 0013 (rev): agents hold context / skills require context types; context types refine structurally; reference-by-id kills raw path refs; on-disk serialization deferred - 0014: Go-only pivot; spec stays normative-in-principle; conformance suite becomes a Go golden-file determinism suite (supersedes dual-impl parts of 0003/0004/0005) - 0015: exposure/visibility (Go export model; only exposed capabilities emit cards; private-by-default) - 0016: sealing/mutability/presence (sealed forecloses modification not extension; open-by-default; no special root) - 0017: tools as extern declarations; the capability/skill/tool trio; MCP crosswalk (our capability -> MCP tool) - 0018: callable-resource card as third ARD archetype; --emit-ard default; publish as an edge verb - 0019: two-doors context lifecycles (compile-time bake vs runtime query; user-written readers) All Proposed; no implementation yet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the .tfer serialization from the ADR batch and a v0 LSP. .tfer format (go/internal/resource): - loader walks .tfer alongside .yaml; each .tfer is YAML frontmatter + a verbatim markdown body split on `---` fences - body materializes onto the resource: a skill's instructions or a context object's content; bodyless kinds reject a non-empty body - new kinds `context` (instance; declares a contextType) and `contextType` (class; optional JSON Schema over frontmatter, may embed to refine) - reference-by-id groundwork; contextType/schema fields; .yaml still loads - validateShape split into a filesystem-independent validateDocumentShape so a single buffer can be checked without the whole source tree; CheckDocument is the diagnostic entrypoint Language server (go/internal/lsp, go/cmd/typeference-lsp): - JSON-RPC 2.0 over stdio; initialize/didOpen/didChange/didSave/didClose/ shutdown/exit; full-document sync - per-document authoring diagnostics via resource.CheckDocument (fence, unknown field, kind, binds, context/contextType shape errors) - v0 scope: single-file shape diagnostics only; cross-tree composition diagnostics (resolution, ambiguity, unsatisfied interfaces) are follow-up Tests cover the frontmatter split, body mapping, new kinds, .yaml/.tfer interop, and an LSP request/response round trip. Conformance suite still passes (byte-identity preserved). Makefile gains a build-lsp target. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wires ADR-0013 typed context and ADR-0017 tool declarations into the resolver. - contextType refinement via embedding: a type satisfies itself plus every type it transitively embeds (governedX embeds X -> satisfies both), with cycle detection - context objects held by id (`context:` on agents/profiles), promoted through embeds like context files - skills declare requiresContextTypes; checked at agent level against the union of held context objects' refinement closures (agent-level, not profile-level, so promoted requirements resolve once the agent supplies context) - new `tool` kind (extern declaration; interface schemas validated); skills declare requiresTools, checked to be declared at agent level - loader: new fields (requiresContextTypes, context, requiresTools, visibility) with kind-scoped validation; `tool` kind registered All additive and opt-in: existing resources set none of these fields, so compiled output and the conformance suite are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- capabilities carry visibility internal (default) or exposed; the resolver marks a bound skill Exposed from its capability's visibility - exposure rides the resolved skill, so it promotes through embedding onto the embedding agent's public surface automatically - ResolvedAgent.ExposedSkills() returns the public callable surface, from which a callable card (ADR-0018) is emitted rather than from every skill Private-by-default; existing resources declare no visibility, so output and conformance are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- skill bindings carry sealed/required; both ride the resolved skill through promotion - a sealed capability may not be overridden by a shallower binding or rebound locally: the resolver tracks sealedBy during promotion and errors on override or local rebind - extension is unaffected: adding adjacent capabilities alongside a sealed one resolves normally (sealed member, open container) Open-by-default; existing bindings set neither flag, so promotion behavior and conformance are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- skills declare either instructions (unimodal) or variants (mode -> {instructions});
the two are mutually exclusive and validated
- resolver carries a mode->instructions map and selects a default rendering
(prefer pipeline > manual > a2a, else alphabetically-first) into Instructions
- neutral bundle emits a variants member ONLY for multimodal skills, so
unimodal bundle output is byte-identical (conformance unaffected)
- .tfer body-as-instructions is rejected for multimodal skills (per-variant
instructions live in frontmatter) per the ADR-0012 cross-constraint
Per-target-surface variant selection in the target adapters (copilot->manual,
etc.) and per-variant requirement narrowing remain follow-ups; the default
variant renders across all targets today.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ostics Expands the language server beyond single-file shape diagnostics: - workspace roots captured at initialize; an id -> file index is built and refreshed on save - composition diagnostics: on open/save the whole workspace is resolved from disk (compile.Validate) and resolution errors (ambiguity, unsatisfied requires, sealing violations, undeclared tools/capabilities) surface on the file they name, or on the active file when unattributable; keystrokes stay fast with single-file shape diagnostics only - textDocument/completion: kind values after `kind:`, top-level field names - textDocument/definition: jump from a resource-id token to its defining file - textDocument/documentSymbol: the resource (kind + id) as a symbol - capabilities advertised accordingly Tests cover completion, token/symbol extraction, definition resolution, document symbols, and a composition diagnostic round trip. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A worked corpus that compiles through the Go implementation and exercises every new construct together: an exposed capability, a multimodal skill (pipeline/ manual/a2a variants), a tool extern with requiresTools, contextType refinement satisfying requiresContextTypes, a sealed binding, context held by id, and a .tfer context note with a markdown body. Validates and builds clean; the three documented negative cases (drop held context, rebind the sealed capability, delete the tool) each fail with a precise diagnostic. Not wired into CI or the playground packer (both target examples/helio), so it is purely additive. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ADR-0014) Executes the Go-only pivot. The specification stays normative in principle; the Go compiler is the reference and only implementation. Determinism — the property behind diff and the committed-digest guarantee — is unchanged; only the second implementation and the cross-implementation half of the conformance suite go away. - remove src/ (C# compiler/CLI), tests/ (C# suite), TypeFerence.slnx, Directory.Build.props - CI: drop the dotnet build/test/conformance jobs; the conformance suite becomes a Go-only determinism suite; keep gofmt/vet/test/wasm, the diff-against-dist determinism check, the self-host drift gate, and the whitepaper docs job - release: drop the dotnet verify step - Makefile: remove build-dotnet/test-dotnet and the dotnet conformance/fmt steps - README, conformance/README: reframe as one implementation + a golden-file determinism suite - ADR-0014 marked Accepted; CHANGELOG updated Go tests, the helio determinism check, and the self-host drift gate all pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
No .yaml resources remain under examples/helio. The 10 non-skill resources are wrapped as .tfer frontmatter (identical parse); the 4 skills move their instructions into the .tfer body, showcasing the format. Compiled bundles are byte-identical — only the ARD source-package entry changes, because it embeds the source files themselves (paths .yaml -> .tfer, media type, recomputed source digest). dist/ard/ai-catalog.json is regenerated to match; `diff examples/helio --against dist` reports no differences. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wrap the remaining .yaml resources as .tfer frontmatter; no .yaml resources remain anywhere under examples/. Validates and builds unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wrap agents/maintainer's 9 resources as .tfer frontmatter. The neutral bundle and the compiled root AGENTS.md are byte-identical, so the self-host drift gate (cmp AGENTS.md) still passes; only dist-maintainer's ARD source-package digest changes, regenerated here. No .yaml resources remain under agents/ or examples/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
--emit-ard now emits a third archetype alongside the source-package and precompiled-bundle entries: a callable-resource card per agent that exposes a capability. The card carries the fully-assembled invocation contract — each exposed capability's dispatch name, description, input/output schemas, and the instruction-package template — plus derivedFrom provenance to the source digest. Emitted only when an agent has an exposed capability, so agents with none add no entry: examples/helio (nothing exposed) is byte-identical and the determinism suite still passes. examples/repo-agent's exposed repository-status now produces a card. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add ResolvedSkill.InstructionsFor(mode): a surface can select a variant's rendering, falling back to the default (ADR-0012) - the callable-resource card is the agent-to-agent surface, so it renders the a2a variant when a skill declares one (helio unchanged — no exposed caps, no cards; repo-agent's card now shows the a2a text) - mark ADRs 0012, 0013, 0015, 0016, 0017, 0018, 0019 Accepted (implemented) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…13/0012) - agents/profiles may declare allowedContextTypes; the effective list is the intersection of non-empty allow-lists through embeds (most restrictive ancestor wins). Held context must satisfy an allowed type via its refinement closure, else a build error. Empty = unrestricted. - variants may declare requiresContextTypes/requiresTools; a multimodal skill's effective requirements are the union of skill-level and every variant's (all variants are emitted, so the agent must satisfy each), checked at agent level and validated for id format at load. Additive/opt-in: no existing resource sets these, so helio stays byte-identical and conformance passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…0012) The neutral target emits one SKILL.<mode>.md per variant for a multimodal skill, alongside the default SKILL.md. Absent for unimodal skills, so their output is unchanged and helio stays byte-identical. repo-agent's repository-status now emits SKILL.pipeline.md, SKILL.manual.md, and SKILL.a2a.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- context objects may carry frontmatter fields beyond the standard keys; the loader collects them (other kinds stay strict — unknown key is still an error) - a contextType's schema enforces its required fields on the object; required fields accumulate across the refinement closure (a governed-cast object must satisfy cast-of-characters' required fields too) - repo-agent showcases it: governed-cast requires `owner`, notes/team carries `owner: Dana`; removing it fails validation Additive: schemaless contextTypes impose nothing, and no existing resource uses typed context fields, so helio stays byte-identical. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A thin extension under editors/vscode that associates .tfer files (YAML frontmatter + markdown body) and launches typeference-lsp for diagnostics, completion, go-to-definition, and document symbols. - package.json (language + grammar contributions, vscode-languageclient dep), extension.js (stdio LSP client, configurable serverPath), a language-configuration and a TextMate grammar injecting source.yaml into the frontmatter and markdown into the body - verified as far as this environment allows: JSON valid, extension.js passes node -c, npm install resolves, vscode-languageclient loads (only the host-provided `vscode` module is unavailable outside the editor). Not yet run end-to-end in an editor — README says so. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
typeference publish <ard-dir> summarizes a compiled ARD catalog (source, bundle, and callable-card entries); with --registry it POSTs the catalog to a registry. Safe by default — a dry run without --registry, no network. This is the deployment edge, explicitly not the deterministic core: registry lifecycle is disclaimed by core semantics, so publish is an optional, side-effecting verb that a user invokes deliberately. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the emission half of typed context: a ResolvedAgent now carries its held context objects as (id, contextType) refs, and the neutral bundle emits a `context` array — conditionally, so agents holding none are byte-identical to before. Reference-by-id context is now visible in compiled output, not just resolved and type-checked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Evolve the flagship example so it demonstrates v3, where the org's own behavior motivates each feature (not box-ticking): - exposure: repository-status, prepare-brief, and triage-message are the agents' public entry points -> visibility: exposed, so both agents emit callable cards - variants: payments-repository-status (the skill actually consumed a2a when prepare-brief calls payments-repo-agent.repository-status) is multimodal (pipeline/manual/a2a); the neutral bundle fans out per-mode SKILL.md and the callable card renders the a2a variant - tools: a repository-signals extern; payments-repository-status requiresTools it - typed context: safety-policy becomes a contextType (schema requires `authority`) and a held context object; triage-message requiresContextTypes it; enterprise- defaults holds it by id (dropping the old contextFile/slot); the bundle lists it - sealing: person-defaults seals the triage-message binding so an embedder cannot weaken the "do not send a reply" rule dist/ is regenerated; `diff examples/helio --against dist` is clean, the self-host gate is unaffected, and the eval dry run still runs. byte-identity was scaffolding during the build; the flagship example now teaches the features. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Context stops being a bibliography and starts being content. A resolved context ref now carries the object's displayName and body; renderInstructions inlines a "## Context" section into every target's agent doc, and the neutral bundle's context entries carry the content. Held typed context (e.g. helio's safety policy) now actually appears in the compiled AGENTS.md the agent reads, instead of being merely referenced — closing the emission half of typed context and the long-standing context-drift gap for context objects. dist regenerated; helio diff clean; conformance unaffected (agents holding no context objects are byte-identical). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ons to Copilot/Cursor (ADR-0012) Implements ADR-0012's target-driven variant selection and fixes a delivery gap the conceptual review surfaced: Copilot and Cursor rendered skill *descriptions* but never skill *instructions*, so those agents were under-specified. - variantForTarget: interactive/human surfaces (Copilot, Cursor, Codex) render the "manual" variant; the neutral bundle keeps the default and fans out. - Copilot/Cursor now inline each skill's instructions (manual variant) into their agent docs, since they have no per-skill SKILL.md mechanism. - Codex's SKILL.md renders the manual variant instead of the default. - fan-out lifted out of the Neutral special-case into a shared writeSkillFiles helper (altitude); dead renderSkill removed. This is a deliberate change to the Copilot/Cursor/Codex output contract, so the golden conformance digests for the 7 skill-bearing fixtures are regenerated and dist is rebuilt. helio diff clean; conformance green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The self-hosted maintainer's own context, capability, skill, and profiles still told it to run the deleted C# implementation (dotnet test TypeFerence.slnx, "both implementations", src/, tests/). The self-host drift gate could not catch this because dist-maintainer was regenerated from the same stale source. Rewrite the maintainer definition for Go-only + the golden-file determinism suite, recompile dist-maintainer and the root AGENTS.md, and drop the stale C# reference in the parity test's comment/name. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ed-diamond doc - remove the accidentally-committed go/typeference-wasm build artifact (7.5 MB) and gitignore it (and *.wasm) - requiredFields now errors on a schema whose "required" is present but not a string array, instead of silently enforcing nothing - pathToURI percent-encodes via url.URL, so workspace paths with spaces/reserved characters produce valid file URIs (go-to-definition no longer breaks there) - soften ADR-0016's "sealed diamond is fine" claim to note the resolver's pre-existing equal-depth ambiguity check rejects any diamond today Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finishes two features the conceptual review flagged as nominal: - Typed context now checks field TYPES, not just required-presence. Context frontmatter fields carry their structural kind (scalar/sequence/mapping); validateContextFields checks each field declared in the contextType schema (and its refinements) matches — a scalar in an array-typed field, etc., is a build error. parseContextSchema reads both required and property types. - The callable card carries the WHOLE invocation surface, not just a2a: each tool keeps a2a as the default instructionsTemplate and now also carries every declared variant's template, and the card's data carries the agent's held context (id, contextType, content). A discovery consumer can invoke in any mode with the agent's context in hand. helio dist regenerated (richer card); conformance unaffected (no fixture exposes a capability, so none emit cards). Not done, and flagged honestly: conforming the card to the actual ARD / A2A Agent-Card schema a real agent finder consumes (needs the external spec), and card signing via the full trust config. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each tool entry in a callable card now carries requiresTools, so a consumer invoking the agent knows which extern tools it must provide — completing the invocation contract. helio dist regenerated; helio diff clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Makes agents discoverable/invokable by real tooling, not just our proprietary
callable card. Per agent that exposes a capability, --emit-ard now writes under
ard/:
- <slug>.agent-card.json — an A2A Agent Card (a2a-protocol.org): protocolVersion,
name, description, version, url, preferredTransport, capabilities,
default{Input,Output}Modes, provider, and skills[] (one per exposed
capability). Because TypeFerence is definition-layer, the required
service-endpoint url is templated by convention (https://<domain>/a2a/<slug>);
the deployer serves the A2A endpoint there or overrides it.
- <slug>.mcp.json — an MCP tool manifest (modelcontextprotocol.io): each exposed
capability as an MCP tool with its input/output schemas embedded as JSON Schema
objects (not strings), directly loadable into an MCP server.
Emitted only for exposed agents, so the conformance fixtures (none exposed) are
unaffected; helio dist gains the four files. This is the piece that lets an agent
show up in an agent finder / stand up an MCP server from a compiled definition.
Still deliberately out of scope: signing the A2A card (JWS over the JCS-
canonicalized card) and per-tool auth/scope — flagged as follow-ups.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ai-catalog.json we emit is structurally ARD-conformant (specVersion 1.0, host, entries with identifier/displayName/type + value-or-reference honored via data), but its entry media types were all proprietary (vnd.typeference.*), so an ARD registry would not recognize the resources as MCP servers or A2A agents. Per exposed agent, the catalog now also emits two entries in the official media types a registry indexes — application/a2a-agent-card+json and application/mcp-server+json — each embedding the resource as `data`, alongside the richer proprietary callable card. The A2A/MCP builders are shared with the standalone ard/<slug>.agent-card.json and ard/<slug>.mcp.json files. helio dist regenerated; conformance unaffected (nothing exposed in fixtures). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…y cards) Makes `compile an agent -> a pushable ai-catalog.json` real, per review feedback. Catalog is discovery, not fulfillment: - drop the proprietary vnd.typeference.callable-card entry entirely. The catalog now carries only standard-typed discovery entries per exposed agent — application/a2a-agent-card+json and application/mcp-server+json (each embedding the resource as `data`, value-or-reference honored) — plus the source-package and target-bundle provenance entries. - the fulfillment that lived in the card (a2a instructions template, every variant, required tools, held context) moves into the MCP manifest's x-typeference extension, so the .mcp.json stays a valid MCP file a generic client reads AND is the artifact a TypeFerence-aware server builds from. That is the definition-vs-deployment split: catalog discovers, server fulfills. Project manifest (the .slnx analogue): - new optional source-root `typeference.yaml` (schemaVersion, name, version, publisher), loaded and excluded from the resource walk like the trust config. - the ARD source-package identity/version now come from the manifest (not the folder name), and a declared `publisher` supplies the domain AND defaults ARD emission on — so `typeference build examples/helio` with no flags emits a pushable dist/ard/ai-catalog.json. - examples/helio gains a manifest; dist regenerated; helio + selfhost diffs clean; conformance unaffected (fixtures expose nothing). Follow-ups unchanged: A2A card signing (JWS/JCS), per-tool auth/scope. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`required` was parsed, resolved, and then dropped: nothing read it, so a declared modifier had no effect. The reason it was unenforceable is structural, not an oversight — embedding only promotes, so nothing in the model can drop a binding, and "cannot be dropped" guarded an operation that does not exist. Give the axis the operation it actually governs: a mandate may be declared without a fulfillment. A binding that omits `ref` and names a `capability` is an abstract requirement — the profile demands it, an embedder supplies it. Profiles may carry unfulfilled requirements (that is what makes them abstract); agents are concrete and must bind every capability mandated by themselves or anything they embed. This is the demand side of the type system: `satisfies` is inferred structurally, so interfaces only observe presence; nothing could require it. `required` stays presence-only — overriding a required-but-open capability is legal, since what fills it is sealing's concern. Also emit sealed/required into bundles when a lock is engaged, so `inspect` can show modifier state and `diff` can flag open -> sealed, as ADR-0016 promised. Fixes a latent determinism bug found while regenerating the reference: the loader did not normalize CRLF, so a .tfer body carried line endings verbatim into artifacts and compiled output depended on the checkout's git configuration rather than on the source. Mirrors compile.readTextFile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two emitted artifacts asserted things about the world that TypeFerence had
no way to know.
The Codex target registered an MCP server invoking `typeference serve`.
There is no `serve` verb — it is a fossil of the C# runtime ADR-0014
retired — so every Codex config ever emitted pointed at nothing.
The A2A agent card templated its service endpoint from the publisher
domain (https://<domain>/a2a/<slug>). Nothing is deployed there. That
matters more than it reads: an A2A card in a registry *is* the agent
picker entry, so publishing one means a picker routes to a URL that 404s.
Both are the same field — the agent-to-execution binding, rendered for
different consumers — so they get the same treatment, applied per the
compile-time declaration / runtime body split tools already use
(ADR-0017): bake what is known, leave a named hole for what is not.
typeference.yaml gains a deployment block (a2aBaseUrl, mcpCommand).
Target configuration is local scaffolding a deployer wires up, so an
undeclared command emits ${TYPEFERENCE_MCP_COMMAND} with instructions.
A discovery card is a live claim the moment it is published, so an
undeclared endpoint emits no card at all rather than one that lies. The
MCP tool manifest asserts no endpoint and is emitted either way.
helio now declares its endpoint. Its agent cards are byte-identical to
before — the compiler simply stopped being the one asserting them.
Regenerates dist, dist-maintainer parity, and the codex conformance
digests: an intended output-contract change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Close the type system, add needed key words, add package management