Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

48 changes: 24 additions & 24 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions docs/validation-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,45 @@ Codes that fire when `vat resources validate` checks external `http(s)://` links
- **Why it matters:** A network-level failure often reflects a transient DNS or connectivity problem on the validating machine rather than a genuinely broken link, so it is surfaced as a warning rather than blocking the build.
- **Fix:** Check the host (DNS, reachability, certificate); or set `severity.EXTERNAL_URL_ERROR` to `ignore` if the host is intentionally unreachable from the validation environment.

## Authenticated External Link Codes

Codes that fire when `vat resources validate` checks external links whose host is claimed by a provider in `resources.linkAuth` (e.g. private GitHub repos, SharePoint files). The validator issues an authenticated request via the configured token source and classifies the response; outcome-to-code mapping is per-provider because hosts disagree about what a 404 means (GitHub masks `403 access-denied` as `404`; Microsoft Graph distinguishes cleanly). All five codes are configurable like any other code (`validation.severity` / `validation.allow`); the provider's `check` block routes an outcome to a *code*, never to a *severity*.

### `LINK_AUTH_DEAD`

- **Default:** `error`
- **What:** An authenticated external link returned `404` or `410` from a host whose provider declares `notFoundMeaning: dead` (i.e. honest-404 hosts like SharePoint). The request was authenticated, so `404` here is not a permissions problem — the resource is missing.
- **Why it matters:** Unlike the anonymous external-URL check, an authenticated 404 against an honest-404 host is high-confidence evidence of link rot, which is why this is the only `LINK_AUTH_*` code that defaults to `error`. The provider declares this property; hosts that mask access-denied as 404 instead use [`LINK_AUTH_DEAD_OR_UNAUTHORIZED`](#link_auth_dead_or_unauthorized).
- **Fix:** Fix or remove the link; or set `severity.LINK_AUTH_DEAD` to `ignore` if the path is expected to be transient.

### `LINK_AUTH_DEAD_OR_UNAUTHORIZED`

- **Default:** `warning`
- **What:** An authenticated external link returned `404` from a host whose provider declares `notFoundMeaning: ambiguous` (i.e. hosts that mask `403 access-denied` as `404`, like GitHub). The link is either rotted *or* inaccessible to the current identity — the response alone cannot distinguish.
- **Why it matters:** GitHub deliberately does not leak existence of private resources via `403`; both "the file doesn't exist" and "you don't have permission to see this file" return `404`. The checker cannot prove rot, so this is a warning rather than an error. Contributors on a repo where some links point at resources they lack access to will see warnings, not red builds.
- **Fix:** Verify the URL by hand (or with a more-privileged token) to disambiguate; fix or remove if rotted; or set `severity.LINK_AUTH_DEAD_OR_UNAUTHORIZED` to `ignore` if cross-identity ambiguity is expected.

### `LINK_AUTH_FORBIDDEN`

- **Default:** `warning`
- **What:** An authenticated external link returned `403`: the configured identity is authenticated, but the resource refuses access. (Only fires for hosts that emit honest `403`s — see `LINK_AUTH_DEAD_OR_UNAUTHORIZED` for hosts that mask access-denied as `404`.)
- **Why it matters:** A `403` is a permissions signal, not link rot. Treating it as an error would block builds whenever a contributor lacks access to some linked resource — common on cross-team docs. The remedy is granting access or switching identity, not editing the link.
- **Fix:** Grant the identity access to the resource; switch to an identity that has access; or set `severity.LINK_AUTH_FORBIDDEN` to `ignore` if cross-identity inaccessibility is expected.

### `LINK_AUTH_UNAUTHORIZED`

- **Default:** `warning`
- **What:** An authenticated external link returned `401`: the token sent with the request was missing, expired, or invalid for the resource's auth scheme.
- **Why it matters:** Usually a configuration or session problem — the token source resolved a value, but the host rejected it. The link itself may be perfectly fine. Strict CI lanes that should fail on stale credentials can promote this to `error`.
- **Fix:** Refresh the token (e.g. `gh auth login`, `az login`); check the `token` config in `resources.linkAuth`; or promote `severity.LINK_AUTH_UNAUTHORIZED` to `error` on strict CI lanes.

### `LINK_AUTH_UNVERIFIED`

- **Default:** `warning`
- **What:** A provider in `resources.linkAuth` claims this host, but no token source resolved — none of the configured env vars or argv commands produced a non-empty value, so no authenticated request was attempted.
- **Why it matters:** External-URL checking is opt-in, so silently skipping links the project asked to check would be misleading. But `unverified` is not link rot — the link may be fine; the validator just couldn't authenticate. The fix is configuration, not link editing. (`unverified` outcomes are never cached, because the result flips the moment a token appears.)
- **Fix:** Configure a `token` source (env var or argv command); log in to the underlying CLI (e.g. `gh auth login`, `az login`); or set `severity.LINK_AUTH_UNVERIFIED` to `ignore` if running without auth is intentional.

## Packaging-Only Codes

*Stance: see [Packaging](./skill-quality-and-compatibility.md#packaging).*
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vibe-agent-toolkit",
"version": "0.1.39-rc.6",
"version": "0.1.39-rc.7",
"type": "module",
"private": true,
"description": "Toolkit for testing and packaging portable AI agents across various LLMs, frameworks, and deployment targets",
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-config/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vibe-agent-toolkit/agent-config",
"version": "0.1.39-rc.6",
"version": "0.1.39-rc.7",
"description": "Agent manifest loading and validation",
"type": "module",
"main": "./dist/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-runtime/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vibe-agent-toolkit/agent-runtime",
"version": "0.1.39-rc.6",
"version": "0.1.39-rc.7",
"type": "module",
"description": "Runtime framework for building and executing portable AI agents",
"keywords": [
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-schema/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vibe-agent-toolkit/agent-schema",
"version": "0.1.39-rc.6",
"version": "0.1.39-rc.7",
"description": "JSON Schema definitions and TypeScript types for VAT agent manifest format",
"type": "module",
"main": "./dist/index.js",
Expand Down
14 changes: 12 additions & 2 deletions packages/agent-schema/schemas/validation-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,12 @@
"FRONTMATTER_UNKNOWN_LINK",
"EXTERNAL_URL_DEAD",
"EXTERNAL_URL_TIMEOUT",
"EXTERNAL_URL_ERROR"
"EXTERNAL_URL_ERROR",
"LINK_AUTH_DEAD",
"LINK_AUTH_DEAD_OR_UNAUTHORIZED",
"LINK_AUTH_FORBIDDEN",
"LINK_AUTH_UNAUTHORIZED",
"LINK_AUTH_UNVERIFIED"
]
}
},
Expand Down Expand Up @@ -175,7 +180,12 @@
"FRONTMATTER_UNKNOWN_LINK",
"EXTERNAL_URL_DEAD",
"EXTERNAL_URL_TIMEOUT",
"EXTERNAL_URL_ERROR"
"EXTERNAL_URL_ERROR",
"LINK_AUTH_DEAD",
"LINK_AUTH_DEAD_OR_UNAUTHORIZED",
"LINK_AUTH_FORBIDDEN",
"LINK_AUTH_UNAUTHORIZED",
"LINK_AUTH_UNVERIFIED"
]
}
}
Expand Down
30 changes: 30 additions & 0 deletions packages/agent-schema/src/validation-codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,36 @@ export const CODE_REGISTRY = {
'Check the host; or set severity: ignore.',
'external_url_error',
),
LINK_AUTH_DEAD: entry(
'error',
'Authenticated external link returned 404/410 from a host that gives honest 404s (notFoundMeaning: dead). Genuine link rot.',
'Fix or remove the link; or set severity.LINK_AUTH_DEAD to ignore if the path is expected to be transient.',
'link_auth_dead',
),
LINK_AUTH_DEAD_OR_UNAUTHORIZED: entry(
'warning',
'Authenticated external link returned 404 from a host that masks access-denied as 404 (notFoundMeaning: ambiguous, e.g. GitHub). The link is either rotted or inaccessible to the current identity — cannot tell which.',
'Verify the URL by hand (or with a more-privileged token) to disambiguate; fix or remove if rotted; or set severity.LINK_AUTH_DEAD_OR_UNAUTHORIZED to ignore if cross-identity ambiguity is expected.',
'link_auth_dead_or_unauthorized',
),
LINK_AUTH_FORBIDDEN: entry(
'warning',
'Authenticated external link returned 403: the configured identity is authenticated but lacks access to that resource. Not link rot.',
'Grant the identity access to the resource; switch to an identity that has access; or set severity.LINK_AUTH_FORBIDDEN to ignore if cross-identity inaccessibility is expected.',
'link_auth_forbidden',
),
LINK_AUTH_UNAUTHORIZED: entry(
'warning',
'Authenticated external link returned 401: the configured token is missing, expired, or invalid.',
"Refresh the token (e.g. `gh auth login`, `az login`); check the `token` config in resources.linkAuth; or promote severity.LINK_AUTH_UNAUTHORIZED to error on strict CI lanes.",
'link_auth_unauthorized',
),
LINK_AUTH_UNVERIFIED: entry(
'warning',
'A provider in resources.linkAuth claims this host, but no token source resolved (none of the configured env/command sources produced a value).',
"Configure a `token` source (env var or argv command); log in to the underlying CLI (e.g. `gh auth login`, `az login`); or set severity.LINK_AUTH_UNVERIFIED to ignore if running without auth is intentional.",
'link_auth_unverified',
),
} as const satisfies Record<string, CodeRegistryEntry>;

export type IssueCode = keyof typeof CODE_REGISTRY;
Expand Down
25 changes: 25 additions & 0 deletions packages/agent-schema/test/docs/validation-codes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';

import { describe, expect, it } from 'vitest';

import { CODE_REGISTRY } from '../../src/validation-codes.js';

const docsPath = fileURLToPath(new URL('../../../../docs/validation-codes.md', import.meta.url));
// Path is derived from `import.meta.url`, not user input — points at the
// repo's docs/validation-codes.md, the source of truth for code reference anchors.
// eslint-disable-next-line security/detect-non-literal-fs-filename
const docs = readFileSync(docsPath, 'utf8');

describe('CODE_REGISTRY ↔ docs/validation-codes.md coverage', () => {
for (const code of Object.keys(CODE_REGISTRY)) {
it(`${code}: docs/validation-codes.md has the matching ### heading`, () => {
expect(docs).toContain(`### \`${code}\``);
});

it(`${code}: entry.reference is the lowercased code anchor`, () => {
const entry = CODE_REGISTRY[code as keyof typeof CODE_REGISTRY];
expect(entry.reference).toBe(`#${code.toLowerCase()}`);
});
}
});
Loading
Loading