{"id":48819149,"url":"https://github.com/foo-ogawa/agent-contracts","last_synced_at":"2026-05-10T13:04:22.661Z","repository":{"id":354955956,"uuid":"1209612378","full_name":"foo-ogawa/agent-contracts","owner":"foo-ogawa","description":"Declarative YAML DSL toolkit for defining, validating, and rendering multi-agent development workflows","archived":false,"fork":false,"pushed_at":"2026-05-01T05:07:20.000Z","size":535,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-01T07:11:41.657Z","etag":null,"topics":["agent","ai-development","cli","handoff","linting","multi-agent","prompt-rendering","typescript","validation","workflow","yaml-dsl"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/agent-contracts","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/foo-ogawa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-13T15:49:28.000Z","updated_at":"2026-05-01T05:06:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/foo-ogawa/agent-contracts","commit_stats":null,"previous_names":["foo-ogawa/agent-contracts"],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/foo-ogawa/agent-contracts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foo-ogawa%2Fagent-contracts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foo-ogawa%2Fagent-contracts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foo-ogawa%2Fagent-contracts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foo-ogawa%2Fagent-contracts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/foo-ogawa","download_url":"https://codeload.github.com/foo-ogawa/agent-contracts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foo-ogawa%2Fagent-contracts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32490815,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","ai-development","cli","handoff","linting","multi-agent","prompt-rendering","typescript","validation","workflow","yaml-dsl"],"created_at":"2026-04-14T14:00:25.641Z","updated_at":"2026-05-01T09:03:57.358Z","avatar_url":"https://github.com/foo-ogawa.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# agent-contracts\n\n[![npm version](https://img.shields.io/npm/v/agent-contracts.svg)](https://www.npmjs.com/package/agent-contracts)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n**Design multi-agent systems as contracts.**\n\n`agent-contracts` is a toolkit for declaratively defining multi-agent development workflows in **YAML DSL**, with **static validation, semantic linting, and prompt rendering**.\n\nIt is designed for teams that need more than “agents that happen to work”.\nIt helps you define, validate, and evolve:\n\n- who each agent is\n- what tasks can be delegated\n- which artifacts exist and who owns them\n- what validations are required\n- how handoffs are structured\n- how prompts are rendered from the design itself\n\nInstead of letting workflow rules live only in prompts and code, `agent-contracts` makes the system **explicit, reviewable, and CI-checkable**.\n\n---\n\n## Why agent-contracts?\n\nMost agent frameworks focus on **runtime execution**.\n\n`agent-contracts` focuses on **design-time guarantees**.\n\nAs multi-agent systems grow, teams usually run into the same problems:\n\n- agent responsibilities become ambiguous\n- handoff rules drift across prompts\n- artifact ownership is unclear\n- validation logic is inconsistent\n- prompts diverge from the intended workflow\n- shared team conventions stop being enforceable\n\n`agent-contracts` addresses this by treating your agent workflow as a **contract**, not just a set of prompts.\n\nYou can think of it as:\n\n- **OpenAPI for multi-agent workflows**\n- **a contract layer above runtime orchestration**\n- **a source of truth for agent roles, handoffs, and artifact flows**\n\n---\n\n## Who this is for\n\n`agent-contracts` is a strong fit for teams that build or operate:\n\n- multi-agent coding workflows\n- spec → implement → audit → release style pipelines\n- internal agent platforms\n- review-heavy or gate-heavy delivery processes\n- agent systems where artifact ownership matters\n- reusable team definitions shared across projects\n\nTypical users include:\n\n- platform teams standardizing agent workflows\n- engineering teams building internal coding/review agents\n- products that require explicit validation and handoff policies\n- teams that want CI enforcement for agent design consistency\n\n---\n\n## Who this is not for\n\n`agent-contracts` is probably **not** the right starting point if you want:\n\n- a single-agent chatbot\n- a quick prompt prototype\n- an all-in-one hosted agent runtime\n- built-in scheduling, memory, tracing, or hosting\n- a purely code-first orchestration style with no declarative spec\n- maximum flexibility with minimal process constraints\n\nIn short:\n\n- if you want to **run agents quickly**, start with a runtime framework\n- if you want to **design multi-agent systems that stay coherent over time**, use `agent-contracts`\n\n---\n\n## What makes it different?\n\n`agent-contracts` does not try to replace every agent framework.\n\nIt occupies a different layer.\n\n### Positioning\n\n| Product / approach | Primary focus | Best fit | How `agent-contracts` differs |\n|---|---|---|---|\n| **OpenAI Agents SDK** | runtime execution with instructions, tools, and handoffs | apps built around agent runtime behavior | `agent-contracts` focuses on design contracts, static guarantees, and artifact relationships |\n| **CrewAI** | agent/task workflow orchestration | teams that want runtime task execution in YAML | `agent-contracts` goes deeper on validation, ownership, inheritance, and renderable design specs |\n| **AutoGen** | code-first multi-agent programming | research or custom orchestration flows | `agent-contracts` is more declarative, reviewable, and CI-oriented |\n| **Google ADK style patterns** | choosing runtime interaction patterns | production systems built around runtime composition | `agent-contracts` is framework-agnostic and centered on workflow design as a contract |\n\nThe key distinction is simple:\n\n\u003e Other frameworks mainly answer: **How do I run these agents?**  \n\u003e `agent-contracts` answers: **What is the allowed structure of this agent system, and how do we keep it correct as it evolves?**\n\nThis positioning is consistent with common industry patterns: some frameworks center the agent runtime, others separate agent definition and task invocation, but `agent-contracts` is strongest as a **design-time contract layer** across those execution models.\n\n---\n\n## Quick Start\n\nDefine your system in a single YAML file:\n\n```yaml\n# agent-contracts.yaml\nversion: 1\nsystem:\n  id: my-project\n  name: My Agent Workflow\n  default_workflow_order: [design, implement]\n\nagents:\n  architect:\n    role_name: \"Architect\"\n    purpose: \"Drive phases and delegate work\"\n    can_invoke_agents: [implementer]\n\n  implementer:\n    role_name: \"Implementer\"\n    purpose: \"Implement features based on specs\"\n\ntasks:\n  implement-feature:\n    description: \"Delegate feature implementation\"\n    target_agent: implementer\n    allowed_from_agents: [architect]\n    workflow: implement\n    input_artifacts: [spec-md]\n    invocation_handoff: task-delegation\n    result_handoff: implementation-result\n\nartifacts:\n  spec-md:\n    type: document\n    owner: architect\n    producers: [architect]\n    editors: [architect]\n    consumers: [implementer]\n    states: [draft, reviewed, approved]\n````\n\nValidate and render:\n\n````bash\nagent-contracts validate\nagent-contracts render -c agent-contracts.config.yaml\n````\n\n\nA working example is available in [`sample/`](./sample), including:\n\n* [`sample/agent-contracts.yaml`](./sample/agent-contracts.yaml)\n* [`sample/agent-contracts.config.yaml`](./sample/agent-contracts.config.yaml)\n* [`sample/templates`](./sample/templates)\n* [`sample/output`](./sample/output)\n\nA multi-team example is available in [`sample/multi-team/`](./sample/multi-team), demonstrating cross-team interface declaration and consumption.\n\n---\n\n## Core concepts\n\n### Agent\n\nAn **Agent** defines who an execution entity is:\n\n* role name\n* purpose\n* capabilities\n* permissions\n* constraints\n* behavioral rules\n* structured content sections (reference material, procedures, criteria)\n\n### Task\n\nA **Task** defines a delegatable unit of work:\n\n* target agent\n* allowed callers\n* workflow\n* input artifacts\n* invocation/result handoffs\n* task-specific execution expectations\n\n### Artifact\n\nAn **Artifact** defines the objects that move through the workflow:\n\n* owner\n* producers\n* editors\n* consumers\n* states\n* required validations\n* visibility\n\n### Tool\n\nA **Tool** defines an invokable CLI/MCP tool:\n\n* kind (cli, mcp, etc.)\n* input/output artifacts\n* invokable_by (which agents can use it)\n* `commands` — structured list of sub-commands with `category`, `reads`, `writes`, and `purpose`\n\n### Workflow\n\nA **Workflow** defines a phase-level execution sequence:\n\n* `description` — human-readable summary\n* `entry_conditions`\n* `trigger`\n* `external_participants` — actors/participants outside the agent system (e.g., User, external advisory)\n* ordered steps (`delegate`, `gate`, `team_task`, `decision`; legacy: `handoff`, `validation`)\n\nWorkflow steps support additional properties:\n\n* `group` — consecutive steps with the same group are rendered as `par` (parallel) blocks in sequence diagrams\n* `max_retries` (delegate steps) — maximum number of full task re-executions (new sessions) allowed per step. Defaults to `0` (no retries), or `1` when a `retry` block is present\n* `max_follow_ups` (delegate steps) — maximum number of lightweight same-session follow-up messages for output format corrections\n* `retry` (delegate steps) — defines a conditional retry loop with `condition`, `fix_task`, and optional `revalidate_task`. These are rendered as recovery instructions in the LLM prompt\n* `routing_key` (decision steps) — the field that determines branch selection. The legacy field `on` is still accepted but deprecated due to YAML 1.1 reserved word collision\n\n### Validation\n\nA **Validation** defines a verification step for an artifact:\n\n* `target_artifact` — the artifact being verified\n* `kind` — the type of verification (see below)\n* `executor_type` — `tool` (automated) or `agent` (agent-driven)\n* `executor` — the tool or agent that runs the validation\n* `blocking` — whether the validation must pass before proceeding\n* `produces_evidence` — optional artifact produced as evidence\n\n#### Validation kinds\n\n| Kind | Purpose | Example |\n|------|---------|---------|\n| `schema` | Structural schema check | JSON Schema validation, OpenAPI lint, SQL syntax |\n| `mechanical` | Automated tool check | CLI linters, diff checks, coverage reports |\n| `semantic` | Meaning-level review | Agent-based review of spec intent, plan coherence |\n| `approval` | Human/agent sign-off gate | Architect approval before implementation |\n| `provenance` | Source derivation verification | Confirm generated artifact derives from its canonical source (e.g., manifest from API contracts) |\n| `traceability` | Cross-artifact link completeness | Verify every spec requirement reaches contracts, tests, and code |\n| `fidelity` | Semantic faithfulness to source | Confirm tests actually verify spec intent, not just structural compliance |\n\n`schema` and `mechanical` are best suited for automated checks via tools. `semantic`, `fidelity`, and `approval` are typically agent-driven. `provenance` and `traceability` can be either tool or agent-based depending on the verification complexity.\n\n### Guardrail\n\nA **Guardrail** declares a cross-cutting constraint:\n\n* description — what is protected\n* scope — which DSL entities it applies to (agents, tasks, tools, artifacts, workflows)\n* rationale — why the constraint exists\n* tags — classification for filtering\n* exemptions — glob patterns or entity IDs exempt from the guardrail\n\n### Guardrail Policy\n\nA **Guardrail Policy** defines enforcement strategy for guardrails:\n\n* rules — array of enforcement rules mapping guardrails to actions\n* Each rule specifies: severity (`critical`/`mandatory`/`warning`/`info`), action (`block`/`warn`/`shadow`/`info`), override permissions\n\n### Handoff Type\n\nA **Handoff Type** defines the schema for inter-agent messages:\n\n* `schema` — a JSON Schema object describing the full message structure\n* `description`\n* `example`\n* `version`\n\nSchemas can use `allOf` with `$ref: \"#/components/schemas/...\"` to compose shared fields (e.g., common envelope) with type-specific properties.\n\n### Components\n\n**Components** provide reusable definitions, following the OpenAPI pattern:\n\n* `components.schemas` — named JSON Schema fragments that can be referenced from anywhere via `$ref: \"#/components/schemas/\u003cname\u003e\"`\n\n---\n\n## Why teams adopt it\n\n### 1. Explicit workflow design\n\nYour architecture stops living only in prompts, code, and tribal knowledge.\n\n### 2. Static guarantees before runtime\n\nYou can catch broken references, invalid ownership, missing validations, and workflow inconsistencies before execution.\n\n### 3. Prompt generation from source of truth\n\nRendered prompts come from the same DSL that defines roles, tasks, artifacts, and policies.\n\n### 4. Reuse across teams and projects\n\nShared base definitions can be extended safely with `extends`.\n\n### 5. Better CI discipline\n\nDesign regressions become testable.\n\n---\n\n## Features\n\n* **Declarative YAML DSL** for multi-agent development workflows\n* **Agent `sections`** for embedding structured reference material, procedures, and criteria directly in agent definitions\n* **Static schema validation**\n* **Reference integrity checks**\n* **Semantic linting**\n* **Structured handoff definitions** with formal JSON Schema and `allOf` composition\n* **Reusable schema components** via `components.schemas` and JSON Pointer `$ref`\n* **Artifact ownership and lifecycle modeling**\n* **Config-driven prompt rendering** with `skip_empty` support for conditional file generation\n* **Variable substitution** via `${vars.xxx}` in DSL values\n* **Inheritance with merge operators via `extends`**\n* **Guardrail definitions** for cross-cutting process constraints\n* **Guardrail policies** with configurable enforcement (block/warn/shadow/info)\n* **Software bindings** (DI) for tool-specific guardrail implementation (Cursor, Git, GitHub)\n* **Guardrail generation** from DSL + policy + bindings via `generate guardrails`\n* **Interface generation** from DSL via `generate interface` for cross-team contracts\n* **Flexible file splitting** via `$ref` (replacement), `$refs` (import + deep-merge), and JSON Pointer `$ref` (in-document)\n* **Multi-team collaboration** via `team_interface` (public boundary), `imports` (team consumption), and `team_task` (cross-team delegation)\n* **YAML safety linting** for reserved word collision detection across YAML 1.1/1.2\n* **`extensions` declarations** with scope, schema validation, and strict enforcement for custom `x-*` fields\n* **`resolve --expand-defaults`** to materialize all Zod schema defaults in output\n* **DSL completeness scoring** with 7 dimensions, text/JSON output, and `--threshold` CI gate\n* **JSON Schema for editor support and external tooling**\n* **CI-friendly workflow checks**\n\n---\n\n## DSL structure\n\nEntities are defined as **maps keyed by ID**.\n\n````yaml\nversion: 1\nextends: \"./base/\"\n\nsystem:\n  id: my-project\n  name: My Agent Workflow\n  default_workflow_order:\n    - analyze\n    - specify\n    - plan\n    - implement\n    - audit\n    - release\n    - reflect\n\nagents: {}\ntasks: {}\nartifacts: {}\ntools: {}\nvalidations: {}\nhandoff_types: {}\nteam_interface:             # optional — multi-team public boundary\n  version: 1\n  accepts:\n    workflows: {}\n  exposes:\n    artifacts: []\nimports: {}                 # optional — consumed team interfaces\nworkflow: {}\npolicies: {}\nguardrails: {}\nguardrail_policies: {}\ncomponents:\n  schemas: {}\n\nextensions:\n  x-flags:\n    type: array\n    items: string\n    description: \"CLI flags for tool commands\"\n  x-path-hint:\n    type: string\n    description: \"Filesystem path hint\"\n    scope: [artifact]\n    schema:\n      type: string\n      minLength: 1\n    required: true\nextensions_strict: false\n````\n\nThis makes definitions easy to merge, extend, and reference by stable identifiers.\n\n### Single-file format\n\n````yaml\nversion: 1\nsystem: { ... }\nagents: { ... }\ntasks: { ... }\nartifacts: { ... }\n````\n\n### Multi-file format (section-level `$ref`)\n\n````yaml\nversion: 1\nextends: \"./base/\"\nsystem:\n  id: my-project\n  name: My Agent Workflow\n  default_workflow_order: [analyze, specify, plan, implement, audit, release, reflect]\n\nagents: { $ref: \"./agents.yaml\" }\ntasks: { $ref: \"./tasks.yaml\" }\nartifacts: { $ref: \"./artifacts.yaml\" }\ntools: { $ref: \"./tools.yaml\" }\nvalidations: { $ref: \"./validations.yaml\" }\nhandoff_types: { $ref: \"./handoff-types.yaml\" }\nworkflow: { $ref: \"./workflow.yaml\" }\npolicies: { $ref: \"./policies.yaml\" }\n````\n\n### Per-entry `$ref`\n\n`$ref` can be used at any object position. This allows splitting individual entries into separate files:\n\n````yaml\nagents:\n  architect: { $ref: \"./agents/architect.yaml\" }\n  implementer: { $ref: \"./agents/implementer.yaml\" }\n  test-writer: { $ref: \"./agents/test-writer.yaml\" }\n````\n\nEach referenced file contains the agent definition directly (without the key):\n\n````yaml\n# agents/architect.yaml\nrole_name: \"Architect\"\npurpose: \"Drive phases and delegate work\"\ncan_invoke_agents: [implementer]\n````\n\n### Directory `$ref`\n\nWhen `$ref` points to a directory, all `*.yaml` / `*.yml` files in the directory are loaded and merged:\n\n````yaml\nagents: { $ref: \"./agents/\" }\n````\n\nEach file in the directory contains one or more keyed entries:\n\n````yaml\n# agents/architect.yaml\narchitect:\n  role_name: \"Architect\"\n  purpose: \"Drive phases and delegate work\"\n````\n\nFiles are loaded in alphabetical order. Conflicting leaf values across files result in an error.\n\n### `$refs` (import and merge)\n\n`$refs` imports multiple files and **deep-merges** them into the containing map.\nUnlike `$ref` (which replaces an object entirely), `$refs` allows mixing inline definitions with external files.\n\n````yaml\nagents:\n  inline-agent:\n    role_name: \"Inline Agent\"\n    purpose: \"Defined right here\"\n  $refs:\n    - \"./agents/architect.yaml\"\n    - \"./agents/implementer.yaml\"\n    - \"./more-agents/\"           # directories are also supported\n````\n\nEach referenced file uses the same keyed format:\n\n````yaml\n# agents/architect.yaml\narchitect:\n  role_name: \"Architect\"\n  purpose: \"Drive phases and delegate work\"\n````\n\n`$refs` can also be used at the root level to compose a DSL from multiple aspect-oriented files:\n\n````yaml\nversion: 1\nsystem:\n  id: my-project\n  name: My Agent Workflow\n  default_workflow_order: [analyze, implement]\n$refs:\n  - \"./agents-core.yaml\"        # agents + artifacts definitions\n  - \"./agents-constraints.yaml\"  # constraints for the same agents\n  - \"./tasks.yaml\"\n````\n\nOverlapping map keys are deep-merged recursively. Conflicting leaf values (scalar or array) result in an error.\n\n| Directive | Type   | Behavior                                                 |\n| --------- | ------ | -------------------------------------------------------- |\n| `$ref`    | string | Replace the object at that position with file contents   |\n| `$ref` (`#/...`) | string | Replace with the value at the given JSON Pointer path within the document |\n| `$refs`   | array  | Import files and deep-merge into the containing map      |\n\n### JSON Pointer `$ref`\n\n`$ref` also supports **in-document references** using JSON Pointer syntax (RFC 6901).\nWhen the value starts with `#/`, it resolves against the root document instead of the file system.\n\n````yaml\ncomponents:\n  schemas:\n    handoff-common:\n      type: object\n      required: [from_agent, to_agent]\n      properties:\n        from_agent: { type: string }\n        to_agent: { type: string }\n\nhandoff_types:\n  task-delegation:\n    version: 1\n    schema:\n      allOf:\n        - $ref: \"#/components/schemas/handoff-common\"\n        - type: object\n          required: [payload]\n          properties:\n            payload:\n              type: object\n              required: [objective]\n              properties:\n                objective: { type: string }\n````\n\nThis is particularly useful for sharing common schema fragments across multiple `handoff_types` entries via `components.schemas`.\n\nJSON Pointer references are resolved in the same processing phase as file `$ref` — before Zod validation. They can be used anywhere in the document, not just within `handoff_types`.\n\n---\n\n## Example: Agent definition\n\n````yaml\nagents:\n  main-architect:\n    role_name: \"Architect\"\n    purpose: \"Drive phases, delegate, make gate decisions, integrate audits\"\n    dispatch_only: true\n    mode: read-only\n    can_read_artifacts:\n      - spec-md\n      - codebase\n      - test-report\n    can_write_artifacts:\n      - review-note\n    can_execute_tools:\n      - spec-impact-check\n    can_perform_validations:\n      - evidence-gate-review\n    can_invoke_agents:\n      - implementer\n      - test-writer\n    can_return_handoffs:\n      - evidence-gate-verdict\n\n    responsibilities:\n      - \"Manage phase progression and gate decisions\"\n    constraints:\n      - \"Never write code directly\"\n\n    sections:\n      - title: \"Delegation Protocol\"\n        content: |\n          You act as the Architect. You NEVER implement or test directly.\n          Instead you delegate to specialist sub-agents.\n````\n\n---\n\n## Example: Task definition\n\n````yaml\ntasks:\n  implement-feature:\n    description: \"Delegate feature implementation\"\n    target_agent: implementer\n    allowed_from_agents:\n      - main-architect\n    workflow: implement\n    input_artifacts:\n      - spec-md\n      - plan-md\n    invocation_handoff: task-delegation\n    result_handoff: dependency-evidence\n    responsibilities:\n      - \"Implement all requirements from spec-md\"\n    execution_steps:\n      - id: read-specs\n        action: \"Read spec-md and design-docs\"\n        reads_artifact: spec-md\n      - id: implement\n        action: \"Implement changes in codebase\"\n        produces_artifact: codebase\n        depends_on: [read-specs]\n      - id: run-db-lint\n        action: \"Run db-lint\"\n        uses_tool: db-lint\n        x-timeout: 120\n    completion_criteria:\n      - \"canonical artifacts updated\"\n````\n\n`x-` prefixed custom properties work at any nesting level — including inside\n`execution_steps`, `rules`, `workflow.steps`, and other nested objects.\n\n### Extension declarations\n\nProjects can declare their custom `x-*` extension fields in the DSL using `extensions`. This makes extensions discoverable, self-documenting, and — optionally — machine-validated:\n\n````yaml\nextensions:\n  x-flags:\n    type: array\n    items: string\n    description: \"CLI flags for tool commands\"\n  x-path-hint:\n    type: string\n    description: \"Filesystem path hint\"\n    scope: [artifact]\n    schema:\n      type: string\n      minLength: 1\n    required: true\n\nextensions_strict: true  # undeclared x-* properties become errors\n````\n\nEach key must start with `x-` (validated at schema level). The declaration supports:\n\n| Field | Type | Default | Description |\n|-------|------|---------|-------------|\n| `type` | `string` | *(required)* | Informational type descriptor |\n| `items` | `string` | — | Item type (for array-typed extensions) |\n| `description` | `string` | — | Human-readable description |\n| `scope` | `string[]` | all node types | Restricts which DSL node types this extension may appear on |\n| `schema` | `object` | — | JSON Schema to validate the extension value |\n| `required` | `boolean` | `false` | Whether the extension must be present on every in-scope entity |\n\n**Scope values**: `root`, `system`, `agent`, `task`, `execution_step`, `artifact`, `tool`, `tool_command`, `validation`, `handoff_type`, `workflow`, `workflow_step`, `policy`, `guardrail`, `guardrail_policy`, `rule`, `escalation_criterion`, `prerequisite`\n\n**`extensions_strict`**: When `true`, any `x-*` property not declared in `extensions` is an error. When `false` (default), undeclared extensions produce a warning.\n\n**Diagnostics**:\n\n| Code | Severity | Trigger |\n|------|----------|---------|\n| `extension-scope-mismatch` | error | Extension used on a node type outside its declared `scope` |\n| `extension-schema-violation` | error | Extension value fails the declared JSON Schema |\n| `extension-required-missing` | error | Required extension missing on an in-scope entity |\n| `undeclared-extension` | warning/error | Extension not declared in `extensions` (error when `extensions_strict: true`) |\n\n\u003e **Backward compatibility:** `x-extensions` and `x-extensions-strict` are still accepted as deprecated aliases. They produce a `deprecated-property` warning and are normalized to `extensions` / `extensions_strict` during validation.\n\n---\n\n## Example: Artifact definition\n\n````yaml\nartifacts:\n  spec-md:\n    type: document\n    description: \"Specification document\"\n    owner: main-architect\n    producers: [main-architect]\n    editors: [main-architect]\n    consumers: [implementer, test-writer]\n    states: [draft, reviewed, approved]\n    required_validations: [spec-semantic-review]\n    visibility: internal\n````\n\n---\n\n## Example: Workflow definition\n\n````yaml\nworkflow:\n  specify:\n    description: \"Externalize requirements — create spec.md from user stories\"\n    entry_conditions:\n      - User story or feature request received\n    trigger: \"User invokes /speckit.specify or asks to create a feature spec.\"\n    steps:\n      - type: delegate\n        task: specify-feature\n        from_agent: main-architect\n      - type: validation\n        validation: spec-semantic-review\n      - type: decision\n        routing_key: evidence-gate-verdict.verdict\n        branches:\n          PASS: [plan]\n          REVISE: [specify-feature]\n````\n\nDecision steps use `routing_key` to specify the field that determines branching. The legacy `on` field is still accepted but deprecated — see [YAML safety](#yaml-safety) below.\n\n---\n\n## Example: Handoff type definition\n\nHandoff types define the schema for inter-agent messages using JSON Schema.\n\n````yaml\nhandoff_types:\n  task-delegation:\n    version: 1\n    description: \"Delegate a task to a sub-agent\"\n    schema:\n      type: object\n      required: [task, objective]\n      properties:\n        task: { type: string }\n        objective: { type: string }\n        constraints:\n          type: array\n          items: { type: string }\n````\n\n### Using `components.schemas` with `allOf`\n\nCommon fields (e.g., `from_agent`, `to_agent`, `run_id`) can be shared across handoff types by placing them in `components.schemas` and composing via `allOf`:\n\n````yaml\ncomponents:\n  schemas:\n    handoff-common:\n      type: object\n      required: [from_agent, to_agent]\n      properties:\n        from_agent: { type: string }\n        to_agent: { type: string }\n        run_id: { type: string }\n\nhandoff_types:\n  task-delegation:\n    version: 1\n    description: \"Delegate a task\"\n    schema:\n      allOf:\n        - $ref: \"#/components/schemas/handoff-common\"\n        - type: object\n          required: [payload]\n          properties:\n            payload:\n              type: object\n              required: [objective]\n              properties:\n                objective: { type: string }\n\n  implementation-result:\n    version: 1\n    description: \"Return implementation results\"\n    schema:\n      allOf:\n        - $ref: \"#/components/schemas/handoff-common\"\n        - type: object\n          required: [payload]\n          properties:\n            payload:\n              type: object\n              required: [result]\n              properties:\n                result: { type: string }\n                evidence:\n                  type: array\n                  items: { type: string }\n````\n\nThe `$ref: \"#/...\"` references are resolved during loading, before validation. The resulting merged schema is then meta-validated as valid JSON Schema.\n\n---\n\n## Inheritance and merge operators\n\n`agent-contracts` supports shared base definitions with project-level overrides through `extends`.\n\n````yaml\nextends: \"./base/\"\n\nagents:\n  implementer:\n    constraints:\n      $append:\n        - \"Use only approved external libraries\"\n\n  designer:\n    role_name: \"Designer\"\n    purpose: \"UI design\"\n\ntasks:\n  implement-feature:\n    execution_steps:\n      $insert_after:\n        target: run-db-lint\n        items:\n          - id: run-contract-pipeline\n            action: \"Run contract pipeline\"\n            uses_tool: api-pipeline\n````\n\nSupported merge operators:\n\n| Operator        | Behavior                                   |\n| --------------- | ------------------------------------------ |\n| `$append`       | Append entries to end of map/array         |\n| `$prepend`      | Prepend entries to beginning of map/array  |\n| `$insert_after` | Insert after element with specified key/id |\n| `$replace`      | Replace entire value                       |\n| `$remove`       | Remove entries by key/id                   |\n| direct value    | Override scalar field                      |\n\n---\n\n## Multi-team collaboration\n\n`agent-contracts` supports multi-team workflows where teams declare public interfaces and consume each other's capabilities.\n\n### Team Interface\n\nA `team_interface` declares what a team exposes to the outside:\n\n````yaml\nteam_interface:\n  version: 1\n  description: \"Backend team public interface\"\n  accepts:\n    workflows:\n      implement:\n        internal_workflow: feature-implement\n        input_handoff: feature-request\n        output_handoff: implementation-result\n        description: \"Request a feature implementation\"\n  exposes:\n    artifacts:\n      - api-contract\n      - build-report\n  constraints:\n    - \"feature-request must include acceptance_criteria\"\n````\n\nKey design decisions:\n\n* **Workflow-level accepts** — external callers invoke a workflow, not individual tasks\n* **Explicit mapping** — `internal_workflow` separates the stable public name from the internal workflow ID\n* **Listing-based exposure** — an entity is external only if listed in `team_interface`\n\n### Imports\n\nA team consumes another team's generated interface via `imports`:\n\n````yaml\nimports:\n  backend:\n    interface: ./teams/backend/team-interface.yaml\n    version: \"\u003e=1\"\n````\n\nImported entities are referenced as `{team_id}.{public_name}` in cross-team workflow steps.\n\n### `team_task` workflow step\n\nCross-team delegation uses the `team_task` step type:\n\n````yaml\nworkflow:\n  execute-tests:\n    steps:\n      - type: team_task\n        to_team: backend\n        workflow: implement\n        handoff: feature-request\n        expects: implementation-result\n        description: \"Delegate implementation to backend team\"\n````\n\n| Field | Description |\n|-------|-------------|\n| `to_team` | Team ID from `imports` |\n| `workflow` | Public workflow name from the imported interface |\n| `handoff` | Handoff type for the request |\n| `expects` | Handoff type for the response |\n\n### Generating a team interface\n\nThe `generate interface` command produces a self-contained `team-interface.yaml`:\n\n````bash\nagent-contracts generate interface -c agent-contracts.config.yaml\nagent-contracts generate interface -c agent-contracts.config.yaml --team backend\nagent-contracts generate interface -c agent-contracts.config.yaml -o custom-output.yaml\nagent-contracts generate interface -c agent-contracts.config.yaml --dry-run\n````\n\nThe output includes:\n\n* Workflow entries with handoff key references\n* A `handoff_types` section containing only schemas referenced by external workflows\n* An `exposes.artifacts` section with type, description, and states\n* Metadata (`team_id`, `team_name`, `version`, `generated_at`)\n\n### Interface drift detection\n\nThe `check` command detects drift between the declared `team_interface` and the generated `team-interface.yaml`:\n\n````bash\nagent-contracts check -c agent-contracts.config.yaml\n````\n\nIf a `team-interface.yaml` exists and differs from what would be regenerated, the check reports drift.\n\nFor managing multiple teams from a single configuration file (shared bindings, vars, and `--team` filtering), see [Multi-team configuration](#multi-team-configuration).\n\n---\n\n## Variable substitution\n\nWhen using `extends` to share a base DSL across projects, base definitions often contain values that differ per project (project name, language, repository URL, etc.).\n\n`vars` in `agent-contracts.config.yaml` lets you define project-specific values that are substituted into DSL string values using `${vars.xxx}` syntax.\n\n### Defining vars\n\nAdd a `vars` section to your config file. Values must be flat string key-value pairs.\n\n````yaml\n# agent-contracts.config.yaml\nvars:\n  project_name: \"my-service\"\n  language: \"TypeScript\"\n  repo_url: \"https://github.com/org/my-service\"\n````\n\n### Using placeholders in DSL\n\nUse `${vars.\u003ckey\u003e}` in any string value within the DSL YAML (base or project).\n\n````yaml\n# base/agent-contracts.yaml\nagents:\n  implementer:\n    purpose: \"Implements features for ${vars.project_name}\"\n    constraints:\n      - \"Use ${vars.language} for all implementations\"\n      - \"Repository: ${vars.repo_url}\"\n````\n\n### Processing order\n\nVariable substitution happens **after** DSL resolution (`extends` merge) and **before** schema validation:\n\n1. Load config (including `vars`)\n2. Resolve DSL (load + merge `extends`)\n3. Substitute `${vars.xxx}` in all string values\n4. Validate schema\n5. Render / lint / check\n\nThis ensures that merged strings from both base and project are substituted, and the resulting values pass schema validation.\n\n### Error handling\n\nIf a placeholder references an undefined variable, the command exits with an error:\n\n````\nVarsSubstitutionError: Undefined variable \"repo_url\" in value \"Repository: ${vars.repo_url}\"\n  Defined vars: project_name, language\n````\n\n### Notes\n\n- Only string values are substituted; object keys are not affected.\n- `vars` is optional. If omitted, no substitution occurs.\n- Patterns that do not match `${vars.\u003ckey\u003e}` (e.g. `${env.HOME}`, `$vars.xxx`, `{{vars.xxx}}`) are left unchanged.\n\n---\n\n## CLI\n\nFor the full CLI reference with all commands, options, arguments, exit codes, and AI agent policies, see the [CLI Reference](docs/cli-reference.md).\n\nThe CLI contract specification is defined in [`cli-contract.yaml`](cli-contract.yaml) using [CLI Contracts](https://github.com/foo-ogawa/cli-contracts).\n\n### Installation\n\n````bash\nnpm install -g agent-contracts\nnpm install -D agent-contracts\nnpx agent-contracts\n````\n\n### Main commands\n\n| Command                           | Description                                            |\n| --------------------------------- | ------------------------------------------------------ |\n| `agent-contracts resolve [path]`  | Resolve `extends` inheritance and output resolved YAML |\n| `agent-contracts validate [path]` | Validate schema and references                         |\n| `agent-contracts lint [path]`     | Run semantic lint                                      |\n| `agent-contracts render`          | Render outputs from config                             |\n| `agent-contracts score [path]`    | Calculate DSL completeness score                       |\n| `agent-contracts generate guardrails` | Generate guardrail artifacts from bindings       |\n| `agent-contracts generate interface` | Generate team interface YAML from DSL |\n| `agent-contracts check`           | Run resolve → validate → lint → render --check         |\n\nThe `[path]` argument defaults to `agent-contracts.yaml` in the current directory.\nIf `-c` / `--config` is specified, the DSL path from the config file is used.\n\nAll commands also accept `--team \u003cid\u003e` to limit execution to a single team when using a [multi-team configuration](#multi-team-configuration).\n\n#### `resolve` options\n\n| Option | Description |\n|--------|-------------|\n| `--format \u003ctext\\|json\u003e` | Output format (default: `text`) |\n| `--expand-defaults` | Expand all Zod default values in output. Fields like `required_validations: []`, `tags: []`, and `can_read_artifacts: []` are written explicitly instead of being silently applied by schema defaults. |\n| `-c, --config \u003cpath\u003e` | Path to `agent-contracts.config.yaml` |\n| `--team \u003cid\u003e` | Limit to one team (multi-team config only) |\n\n#### `score` options\n\n| Option | Description |\n|--------|-------------|\n| `--format \u003ctext\\|json\u003e` | Output format (default: `text`) |\n| `--threshold \u003cnumber\u003e` | Minimum score; exit 1 if below (for CI gates) |\n| `-c, --config \u003cpath\u003e` | Path to `agent-contracts.config.yaml` |\n| `--team \u003cid\u003e` | Limit to one team (multi-team config only) |\n\nThe score command evaluates 7 dimensions:\n\n| Dimension | What it measures | Weight |\n|-----------|-----------------|--------|\n| Artifact validation coverage | % of artifacts with non-empty `required_validations` | High |\n| Task validation coverage | % of tasks with at least one entry in `validations` | High |\n| Guardrail policy coverage | % of guardrails referenced by at least one policy rule | Medium |\n| Workflow validation integration | % of blocking validations referenced in workflow steps or tasks | High |\n| Schema completeness | % of optional fields filled (description, rationale, trigger, etc.) | Low |\n| Cross-reference bidirectionality | % of agent↔artifact, agent↔tool refs that are reciprocated | Medium |\n| Guardrail scope resolution | % of guardrail scope entries that resolve to existing entities | Medium |\n\n### Common usage\n\n````bash\nagent-contracts resolve\nagent-contracts resolve --expand-defaults --format json\nagent-contracts validate\nagent-contracts lint --strict\nagent-contracts score\nagent-contracts score -c agent-contracts.config.yaml --threshold 70\nagent-contracts score --format json\nagent-contracts render -c agent-contracts.config.yaml\nagent-contracts render -c agent-contracts.config.yaml --check\nagent-contracts check -c agent-contracts.config.yaml --strict\nagent-contracts generate interface -c agent-contracts.config.yaml\nagent-contracts generate interface -c agent-contracts.config.yaml --dry-run\nagent-contracts generate interface -c agent-contracts.config.yaml --format json\n````\n\n---\n\n## Config-driven rendering\n\nRendering is configured via `agent-contracts.config.yaml`.\n\n````yaml\ndsl: ./agent-contracts.yaml\n\nvars:\n  project_name: \"my-service\"\n  language: \"TypeScript\"\n  repo_url: \"https://github.com/org/my-service\"\n\nrenders:\n  - template: ./templates/agent-prompt.md.hbs\n    context: agent\n    output: ./output/{agent.id}.md\n\n  - template: ./templates/overview.md.hbs\n    context: system\n    output: ./output/overview.md\n````\n\nThis lets you generate static outputs for:\n\n* agent prompts\n* task specs\n* overviews\n* artifact docs\n* validation docs\n* workflow docs\n\nall from the same resolved DSL.\n\n### Multi-team configuration\n\nWhen several teams (for example backend, QA, infra) are managed from one workspace, you can list every team in a single config file instead of maintaining separate configs.\n\nThis complements the DSL-level [multi-team collaboration](#multi-team-collaboration) features (`team_interface`, `imports`, `team_task`).\n\n````yaml\nteams:\n  _defaults:\n    bindings:\n      - ./bindings/cursor.yaml\n    vars:\n      language: TypeScript\n    paths:\n      cursor_root: .cursor\n    active_guardrail_policy: default-enforcement\n\n  backend:\n    dsl: ./teams/backend/agent-contracts.yaml\n    interface_output: ./teams/backend/team-interface.yaml\n    bindings:\n      - ./teams/backend/bindings/observability.yaml\n    vars:\n      team_name: backend\n\n  qa:\n    dsl: ./teams/qa/agent-contracts.yaml\n    vars:\n      team_name: qa\n````\n\n**`_defaults`:** Reserved meta-entry in the `teams` map. It uses the same schema as team entries except `dsl` is not required. Values are inherited by all teams. The underscore prefix avoids colliding with real team IDs.\n\n**Merge with `_defaults`:**\n\n* `bindings` — `_defaults` bindings are prepended before team-specific bindings\n* `vars` — shallow merge; team values win\n* `paths` — shallow merge; team values win\n* `active_guardrail_policy` — team wins when present\n\nAll commands accept `--team \u003cid\u003e` to run against a single team:\n\n````bash\nagent-contracts validate -c config.yaml              # all teams\nagent-contracts validate -c config.yaml --team backend  # one team\nagent-contracts check -c config.yaml --team qa          # one team\n````\n\nThe `check` command also validates that imported interface files exist on disk (cross-team references).\n\n**Design constraints:**\n\n* `dsl` and `teams` are mutually exclusive at the config root\n* Every team except `_defaults` must specify `dsl`\n* Existing single-team configs (top-level `dsl` only) remain valid unchanged\n\n### Render target options\n\nEach entry in `renders` supports these fields:\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| `template` | string | yes | Path to Handlebars template |\n| `context` | string | yes | Context type (see below) |\n| `output` | string | yes | Output file path (supports `{\u003ccontext\u003e.id}` placeholder) |\n| `include` | string[] | no | Only render these entity IDs (not with `system`) |\n| `exclude` | string[] | no | Skip these entity IDs (not with `system`) |\n| `skip_empty` | boolean | no | When `true`, if the rendered output is empty or whitespace-only, the file is **not written**. If the file already exists, it is **deleted**. |\n\n#### `skip_empty` usage\n\n`skip_empty` is useful when a single template applies to all entities of a context type, but only some entities produce meaningful output.\n\nFor example, when using `context: tool` to generate per-tool scripts, tools without an `x-script` property would produce empty files. With `skip_empty: true`, those files are simply not created:\n\n````yaml\nrenders:\n  - template: ./templates/tool-script.sh.hbs\n    context: tool\n    output: ./output/scripts/{tool.id}.sh\n    skip_empty: true\n````\n\n````handlebars\n{{!-- tool-script.sh.hbs --}}\n{{#if tool.x-script}}\n{{{tool.x-script}}}\n{{/if}}\n````\n\nTools with `x-script` get a generated script file; tools without it produce no file at all.\n\n`skip_empty` also works with `render --check` (drift detection): when the expected output is empty, the check expects the file to **not exist** and reports drift if it does.\n\n### Available context types\n\nEach `context` type provides a different rendering scope:\n\n| Context | Scope | Output | Key variables |\n|---------|-------|--------|---------------|\n| `system` | Single file | `output` as-is | `system`, `dsl`, `guardrailEnforcement`\\*, `bindings`\\* |\n| `agent` | Per agent | `{agent.id}` in output path | `agent`, `receivableTasks`, `delegatableTasks`, `relatedArtifacts`, `relatedTools`, `relatedHandoffTypes`, `mergedBehavior`, `relatedGuardrails`, `relatedValidations`, `dsl` |\n| `task` | Per task | `{task.id}` in output path | `task`, `targetAgent`, `relatedGuardrails`, `relatedValidations`, `dsl` |\n| `artifact` | Per artifact | `{artifact.id}` in output path | `artifact`, `relatedTools`, `relatedValidations`, `relatedGuardrails`, `producerAgents`, `consumerAgents`, `editorAgents`, `createdInWorkflows`, `dsl` |\n| `tool` | Per tool | `{tool.id}` in output path | `tool`, `invokableAgents`, `inputArtifactDetails`, `outputArtifactDetails`, `relatedGuardrails`, `relatedValidations`, `dsl` |\n| `validation` | Per validation | `{validation.id}` in output path | `validation`, `dsl` |\n| `handoff_type` | Per handoff type | `{handoff_type.id}` in output path | `handoff_type`, `relatedTasks`, `dsl` |\n| `workflow` | Per workflow phase | `{workflow.id}` in output path | `workflow`, `relatedAgents`, `relatedTasks`, `relatedTools`, `relatedArtifacts`, `relatedValidations`, `dsl` |\n| `policy` | Per policy | `{policy.id}` in output path | `policy`, `dsl` |\n| `guardrail` | Per guardrail | `{guardrail.id}` in output path | `guardrail`, `dsl` |\n| `guardrail_policy` | Per guardrail policy | `{guardrail_policy.id}` in output path | `guardrail_policy`, `dsl` |\n\n#### Enriched context details\n\n**`workflow` context** collects all entities involved in a phase:\n\n* `relatedTasks` — tasks where `task.workflow` matches this phase\n* `relatedAgents` — agents from task `target_agent`, `allowed_from_agents`, step `from_agent`, and validation executors\n* `relatedTools` — tools from `can_execute_tools` of all related agents, plus `uses_tool` in execution steps\n* `relatedArtifacts` — artifacts from `can_read_artifacts`, `can_write_artifacts`, `input_artifacts`, plus `produces_artifact` and `reads_artifact` in execution steps\n* `relatedValidations` — validations referenced in workflow steps\n\n**`artifact` context** provides ownership and cross-reference data:\n\n* `relatedTools` — tools with this artifact in `input_artifacts` or `output_artifacts`\n* `relatedValidations` — validations targeting this artifact\n* `producerAgents` / `consumerAgents` / `editorAgents` — resolved agent records\n* `createdInWorkflows` — workflow phases where this artifact is written\n\n**`agent` context** provides merged behavioral specs and cross-references:\n\n* `relatedGuardrails` — guardrails bound via `agent.guardrails[]` or guardrail `scope.agents[]`, merged and deduplicated\n* `relatedValidations` — validations from `agent.can_perform_validations`, resolved into full entries (kind, target_artifact, executor_type, blocking)\n\n**`task` context** provides execution details:\n\n* `relatedGuardrails` — guardrails bound via `task.guardrails[]` or guardrail `scope.tasks[]`\n* `relatedValidations` — validations from `task.validations[]`, resolved into full entries\n\n**`tool` context** provides invocation and artifact details:\n\n* `relatedGuardrails` — guardrails bound via `tool.guardrails[]` or guardrail `scope.tools[]`\n* `relatedValidations` — validations where `executor_type` is `\"tool\"` and `executor` matches this tool ID\n* `invokableAgents` — agents listed in `invokable_by`\n* `inputArtifactDetails` / `outputArtifactDetails` — resolved artifact records\n\n**`system` context** includes binding-aware guardrail enforcement data when `bindings` and `active_guardrail_policy` are configured:\n\n* `guardrailEnforcement` — array of enforcement entries, each with `guardrail_id`, `description`, `severity`, `action`, scoped entities (`scoped_agents`, `scoped_tasks`, `scoped_workflows`, `scoped_tools`, `scoped_artifacts`), `allow_override`, `override_requires`, `trigger` (from binding matcher type), and `escalation`\n* `bindings` — array of loaded `SoftwareBinding` objects\n\nThese fields are only populated when the config specifies `bindings` and `active_guardrail_policy`. Existing templates that do not reference these fields are unaffected.\n\n**Matrix helpers** are available in `context: system` templates:\n\n* `guardrailCoverageMatrix` — generates a Guardrail Coverage Matrix table (guardrail × severity × action × scoped entities × trigger × override × escalation)\n* `taskGuardrailMatrix` — generates a Task × Guardrail cross-reference table showing which action applies to each task\n\n### Handlebars helpers\n\nTemplates can use these built-in helpers:\n\n| Helper | Usage | Description |\n|--------|-------|-------------|\n| `eq` | `{{#if (eq a b)}}` | Strict equality |\n| `notEmpty` | `{{#if (notEmpty obj)}}` | True when object has at least one key |\n| `inc` | `{{inc @index}}` | Increment number by 1 (for 1-based indexing) |\n| `yamlBlock` | `{{{yamlBlock obj}}}` | Render value as YAML-formatted text |\n| `lookupPayloadFields` | `{{#each (lookupPayloadFields schema)}}` | Extract schema field info (name, type, required, enum); resolves `allOf` internally |\n| `join` | `{{join arr \", \"}}` | Join array elements with separator |\n| `contains` | `{{#if (contains arr \"x\")}}` | True when array includes value |\n| `groupBy` | `{{#with (groupBy arr \"key\")}}` | Group array elements by field value |\n| `filterByField` | `{{#each (filterByField arr \"field\" \"val\")}}` | Filter array by field match |\n| `keys` | `{{#each (keys obj)}}` | Object keys as array |\n| `values` | `{{#each (values obj)}}` | Object values as array |\n| `size` | `{{size obj}}` | Array length or object key count |\n| `not` | `{{#if (not x)}}` | Boolean negation |\n| `or` | `{{#if (or a b)}}` | Boolean OR (variadic) |\n| `and` | `{{#if (and a b)}}` | Boolean AND (variadic) |\n| `gt` / `gte` / `lt` | `{{#if (gt a b)}}` | Numeric comparisons |\n| `sequenceDiagram` | `{{{sequenceDiagram}}}` or `{{{sequenceDiagram @key ../dsl}}}` | Generate Mermaid sequence diagram. Supports `external_participants`, `group` (par blocks), `retry` (opt blocks), and read-only agent separation into Audit box |\n| `overviewFlowchart` | `{{{overviewFlowchart dsl}}}` | Generate Mermaid graph showing phases → agents/tools/artifacts relationships (system context) |\n\n---\n\n## Guardrail DI system\n\n`agent-contracts` includes a dependency injection system for guardrails that separates **what to protect** from **how to enforce** and **where to output**.\n\n### Architecture\n\n```text\nagent-contracts.yaml (DSL)        agent-contracts.config.yaml\n├─ guardrails:   (what + why)     ├─ bindings: [cursor.yaml, git.yaml, ...]\n├─ guardrail_policies: (how)      ├─ active_guardrail_policy: default\n└─ agents, tasks, ...             ├─ paths: {cursor_root: .cursor, ...}\n                                  └─ vars, renders (existing)\n```\n\n### Guardrail definition\n\nGuardrails declare constraints in the DSL without any implementation details:\n\n````yaml\nguardrails:\n  no-force-push:\n    description: \"Force push to protected branches is forbidden\"\n    scope:\n      tools: [git]\n    rationale: \"Force push destroys commit history\"\n    tags: [branch-protection, safety]\n````\n\n### Guardrail policy\n\nPolicies define enforcement strategies:\n\n````yaml\nguardrail_policies:\n  default-enforcement:\n    rules:\n      - guardrail: no-force-push\n        severity: critical\n        action: block\n      - guardrail: english-only-code\n        severity: warning\n        action: warn\n        allow_override: true\n````\n\n### Software bindings\n\nBindings define software-specific check implementations, output generation, and rendering:\n\n````yaml\n# bindings/cursor.yaml\nsoftware: cursor\nversion: 1\n\nguardrail_impl:\n  no-force-push:\n    checks:\n      - hook_event: beforeShellExecution\n        matcher:\n          type: command_regex\n          pattern: \"git\\\\s+push\\\\s+.*--force\"\n        message: \"Force push is forbidden\"\n\noutputs:\n  hook-script:\n    target: \"{cursor_root}/hooks/evaluate-hook.sh\"\n    mode: write\n    executable: true\n    template: ./templates/cursor-hook-wrapper.sh.hbs\n\nrenders:\n  - context: agent\n    output: \"{cursor_root}/agent-team/{agent.id}.md\"\n    template: ./templates/agent-prompt.md.hbs\n    exclude:\n      - architect\n  - context: system\n    output: \"{cursor_root}/rules/agent-team.mdc\"\n    inline_template: |\n      {{#each agents}}\n      - {{@key}}: {{this.role_name}}\n      {{/each}}\n````\n\n### Binding inheritance\n\nBinding files support `extends` for inheriting and extending a base binding, using the same mechanism as DSL-level `extends`.\n\nA base binding defines shared guardrail implementations and outputs:\n\n````yaml\n# skeleton/bindings/cursor.yaml (base)\nsoftware: cursor\nversion: 1\n\nguardrail_impl:\n  no-force-push:\n    checks:\n      - hook_event: beforeShellExecution\n        matcher:\n          type: command_regex\n          pattern: \"git\\\\s+push\\\\s+.*--force\"\n        message: \"Force push is forbidden\"\n\noutputs:\n  policy-bundle:\n    target: \"{cursor_root}/guardrails/policy.json\"\n    mode: write\n    inline_template: \"{{json resolved_checks}}\"\n````\n\nA project binding extends the base and adds project-specific guardrail implementations:\n\n````yaml\n# project/bindings/cursor.yaml\nextends: ../../skeleton/bindings/cursor.yaml\nsoftware: cursor\nversion: 1\n\nguardrail_impl:\n  lint-on-save:\n    checks:\n      - hook_event: afterFileEdit\n        matcher:\n          type: file_glob\n          pattern: \"**/*.{ts,tsx}\"\n        message: \"TS file edited — lint results attached.\"\n````\n\nThe result is a single merged binding with all guardrail implementations from both base and project.\n\nMerge behavior:\n\n| Field | Behavior |\n|-------|----------|\n| `software` | Project wins |\n| `guardrail_impl` | Map merge by guardrail ID (new IDs added; same ID deep-merged) |\n| `outputs` | Map merge by output ID (project overrides base) |\n| `renders` | Array concatenation (base renders + project renders) |\n| `reporting` | Deep merge (project fields override base) |\n| passthrough fields | Project wins |\n\nAll merge operators (`$append`, `$prepend`, `$insert_after`, `$replace`, `$remove`) work within binding `extends`, the same as DSL `extends`.\n\nChained inheritance (grandparent → parent → child) and both local path (`./`, `../`) and npm package references are supported. Circular extends are detected and rejected.\n\nWhen using binding `extends`, the config only needs to list the child binding:\n\n````yaml\n# agent-contracts.config.yaml\nbindings:\n  - ./bindings/cursor.yaml    # extends base internally\n  - ./bindings/git.yaml\n````\n\n### Config\n\n````yaml\n# agent-contracts.config.yaml\nbindings:\n  - ./bindings/cursor.yaml\n  - ./bindings/git.yaml\n\nactive_guardrail_policy: default-enforcement\n\npaths:\n  cursor_root: .cursor\n  git_hooks_root: scripts/git-hooks\n````\n\n### Binding template context\n\nBoth `outputs` and `renders` templates have access to the full binding generation context:\n\n| Variable | Type | Description |\n|----------|------|-------------|\n| `system` | `{ id, name }` | System metadata |\n| `guardrails` | `Record\u003cstring, Guardrail\u003e` | All guardrail definitions |\n| `policy` | `GuardrailPolicy` | Active guardrail policy |\n| `binding` | `SoftwareBinding` | Current binding |\n| `all_bindings` | `Record\u003cstring, SoftwareBinding\u003e` | All loaded bindings |\n| `vars` | `Record\u003cstring, string\u003e` | Variables from `config.vars` |\n| `paths` | `Record\u003cstring, string\u003e` | Path variables from `config.paths` |\n| `reporting` | `{ commands, fail_open, timeout_ms } \\| null` | Reporting config |\n| `resolved_checks` | `ResolvedCheck[]` | Resolved guardrail checks |\n| `tasks` | `Record\u003cstring, Task\u003e` | All DSL tasks |\n| `artifacts` | `Record\u003cstring, Artifact\u003e` | All DSL artifacts |\n| `agents` | `Record\u003cstring, Agent\u003e` | All DSL agents |\n| `handoff_types` | `Record\u003cstring, HandoffType\u003e` | All DSL handoff types |\n| `workflow` | `Record\u003cstring, Workflow\u003e` | All DSL workflows |\n\nDSL entities include passthrough fields (`x-*` extensions), so custom metadata defined in the DSL is accessible in templates (e.g., `{{agents.implementer.x-team}}`).\n\n### Binding renders\n\nBinding `renders` provide entity-iteration rendering with full DSL context — the same capability as config-level `renders`, but defined within binding YAML files.\n\nEach render target specifies a `context` type and an `output` path pattern:\n\n| Field | Required | Description |\n|-------|----------|-------------|\n| `context` | yes | Entity type: `agent`, `task`, `artifact`, `tool`, `workflow`, `system`, etc. |\n| `output` | yes | Output path with `{entity.id}` and `{paths_var}` expansion |\n| `template` | one of | Path to external `.hbs` template file |\n| `inline_template` | one of | Inline Handlebars template string |\n| `include` | no | Only render these entity IDs |\n| `exclude` | no | Skip these entity IDs |\n| `skip_empty` | no | Delete target if rendered output is empty |\n| `executable` | no | Set file permissions to 0755 |\n\nFor non-`system` contexts, one file is generated per entity (filtered by `include`/`exclude`). The output path supports two types of variable expansion:\n\n- `{agent.id}`, `{task.id}`, etc. — replaced with the current entity ID\n- `{cursor_root}`, `{observability_root}`, etc. — replaced from `config.paths`\n\n**When to use binding renders vs config renders vs binding outputs:**\n\n| Use case | Recommended |\n|----------|-------------|\n| Generate per-entity files (agent prompts, workflow docs) | Binding `renders` or config `renders` |\n| Generate guardrail/policy runtime artifacts | Binding `outputs` |\n| Generate files using DSL data + guardrail data | Binding `renders` (has both) |\n| Simple config without bindings | Config `renders` |\n\nConfig `renders` remains supported and is not deprecated. Binding `renders` offers the advantage of co-locating templates with their binding definition and having access to the full binding context (`vars`, `paths`, `resolved_checks`, etc.) in addition to DSL entities.\n\n### Generate command\n\n````bash\nagent-contracts generate guardrails -c agent-contracts.config.yaml\nagent-contracts generate guardrails -c agent-contracts.config.yaml --binding cursor\nagent-contracts generate guardrails -c agent-contracts.config.yaml --dry-run\n````\n\n---\n\n## Validation model\n\n`agent-contracts` validates your system in multiple layers.\n\n### Schema validation\n\nChecks:\n\n* required fields\n* types\n* enums\n* handoff schema shape (meta-validated as valid JSON Schema via ajv)\n* `allOf` composition in handoff schemas\n* invalid custom properties without `x-` prefix (checked at all nesting levels)\n* `extensions` declaration validation — scope, schema, required, and undeclared checks\n* `extensions_strict` enforcement — reject undeclared `x-*` properties when enabled\n\nCustom properties with `x-` prefix are allowed on any object in the DSL — top-level entities (agents, tasks, artifacts, …), nested objects (rules, execution steps, workflow steps, …), and the root DSL itself.\n\n### YAML safety\n\nThe DSL is expressed in YAML, which introduces risks from YAML 1.1's implicit type coercion. The `yaml-reserved-key-safety` lint rule warns when reserved words appear in positions that may be misinterpreted by non-1.2 parsers.\n\nThe most notable case is the `on` field in decision steps. In YAML 1.1, bare `on` as a mapping key is interpreted as boolean `true`. While `agent-contracts` uses a YAML 1.2 parser internally, DSL consumers (CI tools, editors, other parsers) may use YAML 1.1 parsers.\n\nTo address this:\n\n* Decision steps now support `routing_key` as the preferred field name (replacing `on`)\n* The legacy `on` field is still accepted for backward compatibility but triggers a lint warning\n* Branch keys like `yes`, `no`, `true`, `false` also trigger warnings\n\n````yaml\n# Preferred — safe across all YAML versions\n- type: decision\n  routing_key: evidence-gate-verdict.verdict\n  branches:\n    PASS: [release]\n    FAIL: [fix-violations]\n\n# Deprecated — works but triggers yaml-reserved-key-safety warning\n- type: decision\n  on: evidence-gate-verdict.verdict\n  branches:\n    PASS: [release]\n    FAIL: [fix-violations]\n````\n\n### Reference integrity\n\nChecks:\n\n* cross-entity references\n* owner / producer / editor / consumer validity\n* handoff schema consistency (`required` vs. `properties` alignment)\n* permission alignment between agents and artifacts\n* `team_interface` internal consistency (workflows, handoffs, and exposed artifacts exist in the DSL)\n* cross-team reference validity (`team_task` targets exist in `imports`)\n\n### Semantic lint\n\nChecks:\n\n* bidirectional consistency\n* validation coverage — warns when artifacts lack validations or have empty `required_validations` (fails under `--strict`)\n* artifact-required-validation wiring — verifies every entry in `artifact.required_validations` exists, targets the correct artifact, and is referenced in a workflow step or task\n* task-output-validation completeness — checks that tasks producing artifacts (via `execution_steps.produces_artifact` or agent `can_write_artifacts`) cover those artifacts' `required_validations`\n* workflow graph completeness\n* merge integrity\n* read-only write violations\n* prerequisite readability\n* artifact ownership — `produces_artifact`/`reads_artifact` in execution steps vs. artifact producers/editors/consumers\n* tool commands — `commands[].reads`/`commands[].writes` reference valid artifacts and align with `output_artifacts`\n* semantic validation phase coverage — warns when `semantic` or `fidelity` validations only appear in late workflow phases (e.g., audit) but not earlier phases (e.g., specify, plan)\n* validation executor context wiring — warns when a validation's executor (agent or tool) exists in the DSL but the validation is not surfaced in the executor's prompt context\n* YAML safety — warns when YAML 1.1 reserved words (`on`, `yes`, `no`, `true`, `false`, etc.) are used in positions where they may be misinterpreted by non-1.2 parsers\n* naming/style issues through Spectral rules\n\n#### `--strict` mode\n\nWhen `--strict` is passed to `lint` or `check`, warnings are treated as failures (exit code 1). This is particularly relevant for artifact-centric validation rules — empty `required_validations`, orphaned validation wiring, and incomplete task coverage are all warnings that become blocking under `--strict`.\n\n### Completeness scoring\n\n`agent-contracts score` provides a quantitative assessment of the DSL's completeness. While `validate` checks structural correctness (pass/fail) and `lint` checks semantic quality (warnings/errors), `score` produces a **numeric metric** (0–100) covering validation coverage, schema completeness, cross-reference consistency, and more.\n\nUse `--threshold` in CI to enforce a minimum quality bar:\n\n````bash\nagent-contracts score -c config.yaml --threshold 70\n````\n\n---\n\n## Best used with runtime frameworks\n\n`agent-contracts` works well alongside runtime frameworks and internal agent infrastructure.\n\nA practical model is:\n\n1. define the workflow in YAML\n2. validate and lint it in CI\n3. render prompts and derived docs\n4. execute the workflow in your runtime of choice\n\nThat separation keeps runtime concerns and architecture concerns from being mixed together.\n\n---\n\n## Tech stack\n\n| Category       | Choice                             |\n| -------------- | ---------------------------------- |\n| Language       | TypeScript (ESM, strict mode)      |\n| Schema         | Zod + ajv (JSON Schema meta-validation) |\n| YAML parsing   | yaml                               |\n| Lint           | TypeScript custom rules + Spectral |\n| Templates      | Handlebars                         |\n| CLI            | commander                          |\n| Testing        | Vitest                             |\n| Build          | tsup                               |\n\n---\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoo-ogawa%2Fagent-contracts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffoo-ogawa%2Fagent-contracts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoo-ogawa%2Fagent-contracts/lists"}