Six honest agent-readiness fixes

feat/orank-honest-wins on orchestkit.yonyon.ai. Each change had to be true on its own terms, independent of any score. Where advertising something would have required a claim we cannot back, it was not advertised.

Request simulator B3 bot-UA routing, B1 twin existence

Pick a client and a path. This runs the same decision middleware.ts makes: whether the request wants Markdown, and whether a Markdown twin actually exists at that path.

Client
Path

Markdown alternate link B1

No rel="alternate" existed anywhere. The link is now emitted, but only where mdTarget() genuinely maps a twin.

Before
<link rel="canonical"
      href="…/docs/foundations/overview">

An agent had to already know the "append .md" convention to find the Markdown at all.

After
<link rel="canonical"
      href="…/docs/foundations/overview">
<link rel="alternate" type="text/markdown"
      href="…/docs/foundations/overview.md">

The 13 marketing pages each declare their own alternates, which shallow-replaces the layout default, so none of them inherit a link to a twin they do not have.

Markdown frontmatter B2

Served Markdown carried no identity: the title was only recoverable by parsing the first H1, and nothing said which URL the body belonged to. Every value below is the one the HTML twin already publishes.

Before
# Overview

Start here.

…
After
---
title: "Overview"
description: "Start here."
canonical: "https://orchestkit.yonyon.ai/docs/foundations/overview"
---

# Overview

Start here.

…
Routetitle fromdescription fromcanonical
/api/md (root)SITE.name, the doc's H1root layout metadata.description/
/api/md/*page.data.titlepage.data.description/docs/<slug>
/pricing.mddoc H1PAGE_SUMMARY.pricing, shared with the HTML page/pricing
/auth.mddoc H1its own lead (no HTML twin exists)/auth.md
/api-policy.mdAPI_POLICY_TITLEAPI_POLICY_LEAD/api-policy

A 404 body gets no frontmatter: canonical would have to name a URL that does not resolve.

trustManifest on catalog entries B4

A host-level manifest already existed. A consumer of the ARD catalog decides per resource, not per publisher, so identity has to travel with the entry it is deciding about.

Before: 1 manifest
host       ✓ trustManifest
entries[0]   (none)
entries[1]   (none)
entries[2]   (none)
entries[3]   (none)
entries[4]   (none)
After: 6 manifests
host      ✓ trustManifest
entries[0] ✓
entries[1] ✓
entries[2] ✓
entries[3] ✓
entries[4] ✓

Two claims only, both verifiable without asking us: the did:web resolvable at /.well-known/did.json, and publishedFrom the public repository. Still absent by design: signature (no request-signing bot), attestations (no compliance program), trustSchema (no external governance).

llms.txt npm links B6

Measured 2026-08-23 with a ClaudeBot/1.0 User-Agent. Every reader of llms.txt is a non-browser client by definition, so the link it was handed could never be followed.

URLStatus to a botBody
www.npmjs.com/package/orchestkit403Cloudflare "Just a moment..."
www.npmjs.com/package/@orchestkit/hook-contract403Cloudflare "Just a moment..."
registry.npmjs.org/orchestkit200package document (versions, dist-tags, bin)
registry.npmjs.org/@orchestkit/hook-contract200package document

Both packages genuinely exist. Only the host changed, and the guard test still pins the exact shipping package name from packages/cli/package.json.

Agent Plugins manifest B5

The tempting move was to add $schema to the manifest Claude Code already reads. Measured against the canonical schema, that would have been a false claim. The two specs want the file in two different places, so ork now ships both and neither compromises.

FileRead byExtra keys vs agent-plugins.orgValid
.claude-plugin/plugin.jsonClaude Codeskills, commands, workflowswould fail
plugin.json (plugin root)agent-plugins.org 1.0.0nonevalid

The canonical schema is additionalProperties: false over ten permitted keys. Spec 4.1 and 5.1 place the Agent Plugins manifest at plugin.json in the plugin root; Claude Code reads .claude-plugin/plugin.json. Two paths, two correct files.

Rejected: one file
$schema  ✓ added
skills       (forbidden)
commands     (forbidden)
workflows    (forbidden)
=> names a schema it fails
Shipped: two files
plugin.json
  $schema, name, version,
  description, author,
  homepage, repository,
  license, keywords
=> 0 violations

Also measured and rejected: moving the three keys under extensions makes Claude Code warn "Unknown field 'extensions'. Claude Code ignores it at load time", trading a false claim for inert data.

Deliberately not done

43 test files / 536 tests green in docs/site, 36 of them new. B5 adds a mutation-tested manifest guard (7 injected defects, 7 caught). Typecheck adds no new errors; the two pre-existing ones are unchanged.