Skip to content

Spec gap: no way to author a composition-only manifest where the model is unknowable #256

Description

@imran-siddique

Where this came from

Building an agent-integrity check for GitHub Copilot in agentrust-io/integrations#68. It measures what a repository contributes to an agent's composition: .github/copilot-instructions.md, .github/instructions/**, AGENTS.md anywhere in the tree, .github/skills/*, .claude/skills/*, .agents/skills/*, and MCP config. Those files decide how the coding agent behaves, they arrive by pull request, and they are reviewable.

It cannot emit an Agent Manifest, and I think the reason is a genuine gap rather than a limitation of the integration.

The problem

Level 0 requires all artifact bindings (section on levels: "Software-only | All artifact bindings"), and artifacts.model_identity is artifact 4.

A repository cannot know the model. Copilot's model is chosen at session time by the user's plan and settings. The same repository serves every model a user might select, and the composition the repository contributes is identical across all of them. model_hash is unavailable, and so is provider / model_id / version in any truthful form.

The obvious workarounds are all dishonest in a way this project usually refuses:

  • provider: github, model_id: copilot describes a product, not a model.
  • model_id: unknown asserts a binding to a thing named "unknown".
  • Omitting the artifact makes the manifest non-conformant at every level.

So the integration currently emits nothing and claims no integrates_with, which I would rather fix in the spec than paper over in the integration.

The part that suggests the spec already half-agrees

The verification result vocabulary (section 5.2) is:

"model_identity": "MATCH | PROVIDER_ASSERTED | MISMATCH | NOT_BOUND  -- REQUIRED",

NOT_BOUND exists for every artifact in that result object. But there is no legal way to author a manifest that produces it, because every level requires all bindings present. A verifier can report a state no conformant manifest can reach.

That reads like an internal inconsistency rather than a missing feature.

What I would propose

Three options, roughly in order of how much I like them.

1. A composition-only profile. A manifest that binds a stated subset of artifacts and declares which ones it deliberately does not bind, with verification returning NOT_BOUND for those. Something like:

{
  "@type": "AgentManifest",
  "profile": "composition-only",
  "unbound_artifacts": ["model_identity", "decision_trace", "memory_baseline"],
  "artifacts": { "system_prompt": {...}, "tool_manifest": {...}, "policy_bundle": {...} }
}

The declaration is the point: an unbound artifact is stated, not silently absent, so a verifier can tell "this manifest does not cover the model" from "this manifest is malformed". That is the same distinction we ended up needing in the capture engines, where an unmeasured category had to be labelled rather than rendered as zero.

2. model_attestation_type: "unbound" alongside the existing hash-bound and provider-asserted, with provider / model_id / version nullable in that mode only. Smaller change, and it slots into machinery that already exists. Less general: the same problem applies to decision_trace and memory_baseline for a repository-scoped manifest, and this only fixes the model.

3. Leave the spec alone and say composition-only manifests are out of scope. Also a fine answer. If so, it is worth stating explicitly, because the natural reading of "Agent Manifest describes what an agent IS" invites exactly the attempt I just made.

Why it might matter beyond this one integration

Anything that inspects a repository rather than a running agent hits this: a CI check, a policy gate on a pull request, a marketplace scanning a published skill. Those are all "here is the composition, the model is chosen later by whoever runs it". If that class is in scope for agent-manifest, it needs a way to say so.

Happy to implement whichever direction you prefer.

Activity

  1. imran-siddique commented on Aug 1, 2026

    @imran-siddique
    MemberAuthor

    Decision: option 1, a composition-only profile with declared unbound artifacts.

    Recording the shape so this is actionable without re-deriving it.

    Why this over the narrower fix

    model_attestation_type: "unbound" would have been a smaller change, and it only fixes the model. A repository-scoped manifest has the same problem with decision_trace and memory_baseline: there is no execution to trace and no memory state, because the model and the session do not exist yet. Fixing one field would leave the other two needing the same workaround.

    It also matches a rule that already proved necessary elsewhere in this stack. The capture engines had a defect where an unmeasured category rendered as a measured zero, and the fix was to state the absence rather than let a reader infer it. unbound_artifacts is that rule applied to manifests: an artifact that is deliberately not bound is named, so a verifier can distinguish "this manifest does not cover the model" from "this manifest is malformed".

    Proposed shape

    {
      "@type": "AgentManifest",
      "profile": "composition-only",
      "unbound_artifacts": ["model_identity", "decision_trace", "memory_baseline"],
      "artifacts": {
        "system_prompt": { "...": "..." },
        "policy_bundle": { "...": "..." },
        "tool_manifest": { "...": "..." }
      }
    }

    Suggested rules:

    1. profile defaults to the current full-binding behaviour when absent, so every existing manifest stays conformant and nothing needs reissuing.
    2. unbound_artifacts is REQUIRED and non-empty when profile is composition-only, and MUST NOT list an artifact that also appears in artifacts. Declaring and binding the same artifact is a contradiction, not a preference.
    3. Verification returns NOT_BOUND for each declared artifact, which is the value section 5.2 already defines and which nothing can currently produce.
    4. A composition-only manifest is not eligible for Level 0 or above. Levels assert an agent instance; this asserts a contribution to one. Better to say so than to invent a Level -1.
    5. Conformance tests: a composition-only manifest verifies with NOT_BOUND on declared artifacts; one that omits an artifact without declaring it stays non-conformant; one that both declares and binds the same artifact is rejected.

    Consumer that needs it

    agentrust-io/integrations copilot measures what a repository contributes to GitHub Copilot: .github/copilot-instructions.md, .github/instructions/**, AGENTS.md anywhere in the tree, three skill roots, and MCP config. It currently emits nothing and claims no integrates_with, because every workaround for model_identity would be an unverifiable claim. This unblocks it, and the same applies to any CI gate, policy check, or marketplace scanning a published skill.

    Happy to implement once the shape is agreed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions