agent-contracts 0.14.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -166,6 +166,8 @@ A working example is available in [`sample/`](./sample), including:
166
166
  * [`sample/templates`](./sample/templates)
167
167
  * [`sample/output`](./sample/output)
168
168
 
169
+ A multi-team example is available in [`sample/multi-team/`](./sample/multi-team), demonstrating cross-team interface declaration and consumption.
170
+
169
171
  ---
170
172
 
171
173
  ## Core concepts
@@ -222,7 +224,7 @@ A **Workflow** defines a phase-level execution sequence:
222
224
  * `entry_conditions`
223
225
  * `trigger`
224
226
  * `external_participants` — actors/participants outside the agent system (e.g., User, external advisory)
225
- * ordered steps (delegate, gate, validation, decision)
227
+ * ordered steps (`delegate`, `gate`, `team_task`, `decision`; legacy: `handoff`, `validation`)
226
228
 
227
229
  Workflow steps support additional properties:
228
230
 
@@ -332,7 +334,9 @@ Design regressions become testable.
332
334
  * **Guardrail policies** with configurable enforcement (block/warn/shadow/info)
333
335
  * **Software bindings** (DI) for tool-specific guardrail implementation (Cursor, Git, GitHub)
334
336
  * **Guardrail generation** from DSL + policy + bindings via `generate guardrails`
337
+ * **Interface generation** from DSL via `generate interface` for cross-team contracts
335
338
  * **Flexible file splitting** via `$ref` (replacement), `$refs` (import + deep-merge), and JSON Pointer `$ref` (in-document)
339
+ * **Multi-team collaboration** via `team_interface` (public boundary), `imports` (team consumption), and `team_task` (cross-team delegation)
336
340
  * **YAML safety linting** for reserved word collision detection across YAML 1.1/1.2
337
341
  * **`extensions` declarations** with scope, schema validation, and strict enforcement for custom `x-*` fields
338
342
  * **`resolve --expand-defaults`** to materialize all Zod schema defaults in output
@@ -368,6 +372,13 @@ artifacts: {}
368
372
  tools: {}
369
373
  validations: {}
370
374
  handoff_types: {}
375
+ team_interface: # optional — multi-team public boundary
376
+ version: 1
377
+ accepts:
378
+ workflows: {}
379
+ exposes:
380
+ artifacts: []
381
+ imports: {} # optional — consumed team interfaces
371
382
  workflow: {}
372
383
  policies: {}
373
384
  guardrails: {}
@@ -830,6 +841,107 @@ Supported merge operators:
830
841
 
831
842
  ---
832
843
 
844
+ ## Multi-team collaboration
845
+
846
+ `agent-contracts` supports multi-team workflows where teams declare public interfaces and consume each other's capabilities.
847
+
848
+ ### Team Interface
849
+
850
+ A `team_interface` declares what a team exposes to the outside:
851
+
852
+ ````yaml
853
+ team_interface:
854
+ version: 1
855
+ description: "Backend team public interface"
856
+ accepts:
857
+ workflows:
858
+ implement:
859
+ internal_workflow: feature-implement
860
+ input_handoff: feature-request
861
+ output_handoff: implementation-result
862
+ description: "Request a feature implementation"
863
+ exposes:
864
+ artifacts:
865
+ - api-contract
866
+ - build-report
867
+ constraints:
868
+ - "feature-request must include acceptance_criteria"
869
+ ````
870
+
871
+ Key design decisions:
872
+
873
+ * **Workflow-level accepts** — external callers invoke a workflow, not individual tasks
874
+ * **Explicit mapping** — `internal_workflow` separates the stable public name from the internal workflow ID
875
+ * **Listing-based exposure** — an entity is external only if listed in `team_interface`
876
+
877
+ ### Imports
878
+
879
+ A team consumes another team's generated interface via `imports`:
880
+
881
+ ````yaml
882
+ imports:
883
+ backend:
884
+ interface: ./teams/backend/team-interface.yaml
885
+ version: ">=1"
886
+ ````
887
+
888
+ Imported entities are referenced as `{team_id}.{public_name}` in cross-team workflow steps.
889
+
890
+ ### `team_task` workflow step
891
+
892
+ Cross-team delegation uses the `team_task` step type:
893
+
894
+ ````yaml
895
+ workflow:
896
+ execute-tests:
897
+ steps:
898
+ - type: team_task
899
+ to_team: backend
900
+ workflow: implement
901
+ handoff: feature-request
902
+ expects: implementation-result
903
+ description: "Delegate implementation to backend team"
904
+ ````
905
+
906
+ | Field | Description |
907
+ |-------|-------------|
908
+ | `to_team` | Team ID from `imports` |
909
+ | `workflow` | Public workflow name from the imported interface |
910
+ | `handoff` | Handoff type for the request |
911
+ | `expects` | Handoff type for the response |
912
+
913
+ ### Generating a team interface
914
+
915
+ The `generate interface` command produces a self-contained `team-interface.yaml`:
916
+
917
+ ````bash
918
+ agent-contracts generate interface -c agent-contracts.config.yaml
919
+ agent-contracts generate interface -c agent-contracts.config.yaml --team backend
920
+ agent-contracts generate interface -c agent-contracts.config.yaml -o custom-output.yaml
921
+ agent-contracts generate interface -c agent-contracts.config.yaml --dry-run
922
+ ````
923
+
924
+ The output includes:
925
+
926
+ * Workflow entries with handoff key references
927
+ * A `handoff_types` section containing only schemas referenced by external workflows
928
+ * An `exposes.artifacts` section with type, description, and states
929
+ * Metadata (`team_id`, `team_name`, `version`, `generated_at`)
930
+
931
+ ### Interface drift detection
932
+
933
+ The `check` command detects drift between the declared `team_interface` and the generated `team-interface.yaml`:
934
+
935
+ ````bash
936
+ agent-contracts check -c agent-contracts.config.yaml
937
+ ````
938
+
939
+ If a `team-interface.yaml` exists and differs from what would be regenerated, the check reports drift.
940
+
941
+ For managing multiple teams from a single configuration file (shared bindings, vars, and `--team` filtering), see [Multi-team configuration](#multi-team-configuration).
942
+
943
+ ---
944
+
833
945
  ## Variable substitution
834
946
 
835
947
  When using `extends` to share a base DSL across projects, base definitions often contain values that differ per project (project name, language, repository URL, etc.).
@@ -911,11 +1023,14 @@ npx agent-contracts
911
1023
  | `agent-contracts render` | Render outputs from config |
912
1024
  | `agent-contracts score [path]` | Calculate DSL completeness score |
913
1025
  | `agent-contracts generate guardrails` | Generate guardrail artifacts from bindings |
1026
+ | `agent-contracts generate interface` | Generate team interface YAML from DSL |
914
1027
  | `agent-contracts check` | Run resolve → validate → lint → render --check |
915
1028
 
916
1029
  The `[path]` argument defaults to `agent-contracts.yaml` in the current directory.
917
1030
  If `-c` / `--config` is specified, the DSL path from the config file is used.
918
1031
 
1032
+ All commands also accept `--team <id>` to limit execution to a single team when using a [multi-team configuration](#multi-team-configuration).
1033
+
919
1034
  #### `resolve` options
920
1035
 
921
1036
  | Option | Description |
@@ -923,6 +1038,7 @@ If `-c` / `--config` is specified, the DSL path from the config file is used.
923
1038
  | `--format <text\|json>` | Output format (default: `text`) |
924
1039
  | `--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. |
925
1040
  | `-c, --config <path>` | Path to `agent-contracts.config.yaml` |
1041
+ | `--team <id>` | Limit to one team (multi-team config only) |
926
1042
 
927
1043
  #### `score` options
928
1044
 
@@ -931,6 +1047,7 @@ If `-c` / `--config` is specified, the DSL path from the config file is used.
931
1047
  | `--format <text\|json>` | Output format (default: `text`) |
932
1048
  | `--threshold <number>` | Minimum score; exit 1 if below (for CI gates) |
933
1049
  | `-c, --config <path>` | Path to `agent-contracts.config.yaml` |
1050
+ | `--team <id>` | Limit to one team (multi-team config only) |
934
1051
 
935
1052
  The score command evaluates 7 dimensions:
936
1053
 
@@ -957,6 +1074,9 @@ agent-contracts score --format json
957
1074
  agent-contracts render -c agent-contracts.config.yaml
958
1075
  agent-contracts render -c agent-contracts.config.yaml --check
959
1076
  agent-contracts check -c agent-contracts.config.yaml --strict
1077
+ agent-contracts generate interface -c agent-contracts.config.yaml
1078
+ agent-contracts generate interface -c agent-contracts.config.yaml --dry-run
1079
+ agent-contracts generate interface -c agent-contracts.config.yaml --format json
960
1080
  ````
961
1081
 
962
1082
  ---
@@ -994,6 +1114,62 @@ This lets you generate static outputs for:
994
1114
 
995
1115
  all from the same resolved DSL.
996
1116
 
1117
+ ### Multi-team configuration
1118
+
1119
+ When 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.
1120
+
1121
+ This complements the DSL-level [multi-team collaboration](#multi-team-collaboration) features (`team_interface`, `imports`, `team_task`).
1122
+
1123
+ ````yaml
1124
+ teams:
1125
+ _defaults:
1126
+ bindings:
1127
+ - ./bindings/cursor.yaml
1128
+ vars:
1129
+ language: TypeScript
1130
+ paths:
1131
+ cursor_root: .cursor
1132
+ active_guardrail_policy: default-enforcement
1133
+
1134
+ backend:
1135
+ dsl: ./teams/backend/agent-contracts.yaml
1136
+ interface_output: ./teams/backend/team-interface.yaml
1137
+ bindings:
1138
+ - ./teams/backend/bindings/observability.yaml
1139
+ vars:
1140
+ team_name: backend
1141
+
1142
+ qa:
1143
+ dsl: ./teams/qa/agent-contracts.yaml
1144
+ vars:
1145
+ team_name: qa
1146
+ ````
1147
+
1148
+ **`_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.
1149
+
1150
+ **Merge with `_defaults`:**
1151
+
1152
+ * `bindings` — `_defaults` bindings are prepended before team-specific bindings
1153
+ * `vars` — shallow merge; team values win
1154
+ * `paths` — shallow merge; team values win
1155
+ * `active_guardrail_policy` — team wins when present
1156
+
1157
+ All commands accept `--team <id>` to run against a single team:
1158
+
1159
+ ````bash
1160
+ agent-contracts validate -c config.yaml # all teams
1161
+ agent-contracts validate -c config.yaml --team backend # one team
1162
+ agent-contracts check -c config.yaml --team qa # one team
1163
+ ````
1164
+
1165
+ The `check` command also validates that imported interface files exist on disk (cross-team references).
1166
+
1167
+ **Design constraints:**
1168
+
1169
+ * `dsl` and `teams` are mutually exclusive at the config root
1170
+ * Every team except `_defaults` must specify `dsl`
1171
+ * Existing single-team configs (top-level `dsl` only) remain valid unchanged
1172
+
997
1173
  ### Render target options
998
1174
 
999
1175
  Each entry in `renders` supports these fields:
@@ -1414,6 +1590,8 @@ Checks:
1414
1590
  * owner / producer / editor / consumer validity
1415
1591
  * handoff schema consistency (`required` vs. `properties` alignment)
1416
1592
  * permission alignment between agents and artifacts
1593
+ * `team_interface` internal consistency (workflows, handoffs, and exposed artifacts exist in the DSL)
1594
+ * cross-team reference validity (`team_task` targets exist in `imports`)
1417
1595
 
1418
1596
  ### Semantic lint
1419
1597