# artifact-contracts

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Declarative artifact registry — define file physical properties, resolve paths to artifact IDs, and detect definition overlaps. Part of the `*-contracts` family alongside [cli-contracts](https://www.npmjs.com/package/cli-contracts) and [agent-contracts](https://www.npmjs.com/package/agent-contracts).

```text
artifact-contracts          cli-contracts
(file properties)            (command interfaces)
      ▲                           ▲
      │ $ref                       │ cli_contract
      │                           │
      └───────── agent-contracts ──┘
                (team contracts)
```

artifact-contracts answers one question: **"What are the files in this project?"** It declares the physical nature of every file — type, authority, editability, change control — without referencing tools, agents, or commands. agent-contracts references artifact IDs when assigning ownership and permissions.

## Quick Start

```bash
npm install artifact-contracts
```

Create `artifact-contracts.yaml` in your project root:

```yaml
artifact_contracts: 0.1.0

system:
  id: my-project
  name: My Project

artifacts:
  application-code:
    type: source
    authority: canonical
    path_patterns:
      - "src/**/*.ts"
    exclude_patterns:
      - "src/generated/**"

  generated-api:
    type: generated-code
    authority: generated
    path_patterns:
      - "src/generated/**/*.ts"

  migration-files:
    type: source
    authority: canonical
    change_control: approval-required
    path_patterns:
      - "db/migrations/**/*.sql"
```

```bash
# Validate definitions
npx artifact-contracts validate

# Check for file overlaps against real repository files
npx artifact-contracts validate --check-files

# Ask "what is this file?"
npx artifact-contracts explain src/generated/client.ts
# → artifact: generated-api
#   authority: generated
#   manual edit: forbidden
#   change control: regeneration-required

# LLM-powered semantic audit (requires agent-contracts-runtime + API key)
npx artifact-contracts audit --adapter openai
```

## Design Principles

- **Pure artifact registry**: Declares file properties only. No references to tools, agents, or commands — no circular dependencies
- **Path → artifact ID resolution**: Every file in the project resolves to at most one artifact ID. Ambiguity (overlap) is a definition error
- **Authority-driven defaults**: `authority` determines sensible defaults for `manual_edit` and `change_control`, reducing boilerplate
- **Agent-native**: Structured output for all commands. LLM agents and CI systems consume the same interface as humans

## DSL Schema

### Required Fields

| Property | Type | Description |
|----------|------|-------------|
| `type` | string | Artifact kind: `source`, `generated-code`, `generated-doc`, `generated-config`, `config`, `lockfile`, `schema`, etc. |
| `authority` | enum | `canonical` (human-authored SSoT), `derived` (derived from SSoT), `generated` (fully tool-generated), `control` (configuration/settings) |
| `path_patterns` | string[] | Glob patterns for files belonging to this artifact |

### Optional Fields

| Property | Type | Description |
|----------|------|-------------|
| `description` | string | Human-readable description |
| `manual_edit` | enum | `allowed`, `discouraged`, `forbidden`. Default based on authority |
| `change_control` | enum | `none`, `approval-required`, `regeneration-required`. Default based on authority |
| `exclude_patterns` | string[] | Glob patterns to exclude from `path_patterns` |
| `visibility` | enum | `public`, `internal`, `private`. Default: `internal` |
| `states` | string[] | Valid states for this artifact |

### Authority Defaults

`authority` determines sensible defaults. Explicit values override these.

| authority | manual_edit | change_control | Typical use |
|-----------|-------------|----------------|-------------|
| `canonical` | `allowed` | `none` | Human-authored source of truth |
| `derived` | `forbidden` | `regeneration-required` | Derived from another SSoT |
| `generated` | `forbidden` | `regeneration-required` | Fully tool-generated files |
| `control` | `allowed` | `approval-required` | Configuration and settings |

### Artifact ID Naming

IDs are YAML keys used as stable identifiers across `agent-contracts` and `cli-contracts`.

- Characters: lowercase alphanumeric and hyphens (`[a-z0-9-]+`)
- Format: `kebab-case`
- Reserved: IDs containing `.` are reserved for future namespace extension

### Path Resolution

A file matches an artifact if it matches any `path_patterns` glob AND does not match any `exclude_patterns` glob.

```yaml
application-code:
  path_patterns: ["src/**/*.ts"]
  exclude_patterns: ["src/generated/**"]

generated-api:
  path_patterns: ["src/generated/**/*.ts"]
```

If a file matches multiple artifacts, it is an **overlap** — a definition error detected by `validate --check-files`.

### Variable Expansion

`artifact-contracts.config.yaml` provides variables that expand `${vars.*}` references in the DSL:

```yaml
# artifact-contracts.config.yaml
variables:
  doc_patterns:
    - "docs/**/*.md"
    - "packages/*/README.md"
```

```yaml
# artifact-contracts.yaml
documentation:
  path_patterns:
    - "${vars.doc_patterns}"
  # expands to: ["docs/**/*.md", "packages/*/README.md"]
```

### Trace Link Rules

The optional `trace` section declares traceability links between artifacts — how one artifact connects to or derives from another.

```yaml
trace:
  links:
    - id: contract-to-generated
      from: cli-contract-definition
      to: generated-cli
      resolver: codegen
      description: "CLI contract generates TypeScript scaffolding"
    - id: source-to-tests
      from: core-library
      to: test-suite
      resolver: naming
```

| Field | Required | Description |
|-------|----------|-------------|
| `id` | Yes | Unique link identifier (kebab-case) |
| `from` | Yes | Source artifact ID (must exist in `artifacts`) |
| `to` | Yes | Target artifact ID (must exist in `artifacts`) |
| `resolver` | Yes | Link resolution strategy |
| `description` | No | Human-readable explanation |

**Resolver types:**

| Resolver | Use case |
|----------|----------|
| `operationId` | ID-based matching (e.g., CLI contract operationId → generated handler) |
| `ast` | Import/call resolution via AST analysis |
| `naming` | Naming convention (e.g., `foo.ts` → `foo.test.ts`) |
| `codegen` | Codegen pipeline (e.g., DSL YAML → generated TypeScript) |

Validation rules:
- `from` and `to` must reference existing artifact IDs
- Link `id` values must be unique
- Self-links (`from === to`) are rejected

## Commands

### Deterministic Commands

| Command | Description |
|---------|-------------|
| `validate [--check-files]` | Schema validation, ID naming check, and optional file-based overlap detection |
| `resolve [--format yaml\|json]` | Output fully-resolved definitions with authority-based defaults applied |
| `list [--authority TYPE] [--path FILE]` | List artifacts, optionally filtered by authority or reverse-lookup by path |
| `explain <path> [--format text\|json\|yaml]` | Show full governance properties for a file path |

### LLM-Powered Commands

| Command | Description |
|---------|-------------|
| `audit [--adapter NAME]` | Semantic quality audit — naming consistency, authority appropriateness, coverage gaps |
| `discover [--adapter NAME] [--write]` | LLM-based artifact discovery — analyzes project structure and generates/updates `artifact-contracts.yaml` |

LLM commands require [agent-contracts-runtime](https://www.npmjs.com/package/agent-contracts-runtime) (optional peer dependency) and an adapter API key. Use `--show-prompt` or `--dry-run` to inspect the prompt without calling the LLM.

```bash
# Install LLM runtime
npm install agent-contracts-runtime

# Run semantic audit
npx artifact-contracts audit --adapter openai

# Preview the prompt
npx artifact-contracts audit --show-prompt
```

```bash
# Discover artifacts from project structure
npx artifact-contracts discover --adapter openai

# Write directly to artifact-contracts.yaml
npx artifact-contracts discover --adapter openai --write

# Preview prompt without LLM call
npx artifact-contracts discover --show-prompt
```

The `discover` command has two modes:
- **init** — When no `artifact-contracts.yaml` exists, creates a registry from scratch by analyzing the file tree
- **update** — When the file exists, adds definitions for uncovered files while preserving existing artifacts

### Utility Commands

| Command | Description |
|---------|-------------|
| `artifact-contracts extract [--all] [commands...]` | Extract embedded CLI contract specification |
| `artifact-contracts agents [--format json\|yaml]` | Output resolved agent-contracts DSL |

All LLM commands support `--log-file <path>` (`-l`) to write structured progress logs.

### Exit Codes

| Code | Meaning |
|------|---------|
| `0` | Success |
| `1` | Validation errors / general error |
| `2` | Overlap detected (path matches multiple artifacts) |
| `3` | Config or input file not found / parse error |
| `10` | Audit findings above `--fail-on` threshold |
| `11` | `agent-contracts-runtime` not installed |
| `12` | Adapter initialization error (missing API key, etc.) |

## CI Integration

```yaml
name: Artifact Registry Check
on:
  pull_request:
    paths: ['artifact-contracts.yaml', 'artifact-contracts.config.yaml']

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npx artifact-contracts validate --check-files
```

## Programmatic API

```typescript
import {
  loadDocument,
  resolveDocument,
  lookupByPath,
  validateDocument,
} from "artifact-contracts/core";

// Load and resolve definitions
const loadResult = loadDocument();
const resolved = resolveDocument(loadResult);

// Reverse-lookup: path → artifact ID
const matches = lookupByPath("src/generated/client.ts", resolved.artifacts);
console.log(matches[0].id);        // "generated-api"
console.log(matches[0].artifact);  // { type, authority, manual_edit, ... }

// Validate
const result = await validateDocument(loadResult, { checkFiles: true });
if (!result.valid) {
  console.error(result.diagnostics);
}
```

## Agent-Readable Interface

Tool capabilities are described in machine-readable form via [cli-contract.yaml](cli-contract.yaml). LLM agents can introspect available commands, their effects, exit codes, and output schemas without parsing help text.

Agent and task definitions for LLM commands are in [dsl/](dsl/) using the [agent-contracts](https://www.npmjs.com/package/agent-contracts) DSL.

## Technology Stack

| Component | Technology |
|-----------|-----------|
| Language | TypeScript (Node.js) |
| Schema validation | [zod](https://github.com/colinhacks/zod) |
| Glob matching | [minimatch](https://github.com/isaacs/minimatch) |
| CLI framework | [commander](https://github.com/tj/commander.js) |
| CLI contract | [cli-contracts](https://www.npmjs.com/package/cli-contracts) |
| Agent DSL | [agent-contracts](https://www.npmjs.com/package/agent-contracts) |
| LLM integration | [agent-contracts-runtime](https://www.npmjs.com/package/agent-contracts-runtime) (optional peer dep) |

## License

MIT
