Skip to content

Commit a96b610

Browse files
Zacclaude
andcommitted
Replace minimal inline operator with full operator.v0 contract
Creates static operator.json with all 10 refusal codes, full CLI surface, and pipeline position. Switches --describe to include_str!. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 24101eb commit a96b610

File tree

3 files changed

+84
-15
lines changed

3 files changed

+84
-15
lines changed

operator.json

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"schema_version": "operator.v0",
3+
"name": "canon",
4+
"version": "0.1.0",
5+
"description": "Canonical identifier resolution — maps raw IDs to canonical forms using a versioned registry",
6+
"repository": "https://github.com/cmdrvl/canon",
7+
"license": "MIT",
8+
"agent_guide": "https://github.com/cmdrvl/.github/blob/main/profile/AGENT_PROMPT.md",
9+
10+
"invocation": {
11+
"binary": "canon",
12+
"usage": [
13+
"canon <INPUT> --registry <DIR> --column <COL> [OPTIONS]"
14+
],
15+
"output_mode": "mixed",
16+
"output_schema": "canon.v0",
17+
"json_flag": null
18+
},
19+
20+
"arguments": [
21+
{
22+
"name": "input",
23+
"type": "file_path",
24+
"required": true,
25+
"position": 0,
26+
"description": "Input CSV or JSONL file. Format detected by extension (.csv, .tsv, .jsonl, .ndjson). Use - for stdin (JSONL only)."
27+
}
28+
],
29+
30+
"options": [
31+
{ "name": "registry", "flag": "--registry", "type": "directory_path", "required": true, "description": "Registry directory containing registry.json and mapping files" },
32+
{ "name": "column", "flag": "--column", "type": "string", "required": true, "description": "Column containing IDs to resolve" },
33+
{ "name": "emit", "flag": "--emit", "type": "string", "default": "json", "description": "Output mode: json (JSON mapping) or csv (CSV with canonical column)" },
34+
{ "name": "canon_column", "flag": "--canon-column", "type": "string", "description": "Name of appended canonical column (default: <COLUMN>__canon). CSV mode only." },
35+
{ "name": "map_out", "flag": "--map-out", "type": "file_path", "description": "Write JSON mapping artifact to file (sidecar). CSV mode only." },
36+
{ "name": "max_rows", "flag": "--max-rows", "type": "integer", "description": "Refuse if input exceeds N data rows" },
37+
{ "name": "max_bytes", "flag": "--max-bytes", "type": "integer", "description": "Refuse if input exceeds N bytes" },
38+
{ "name": "no_witness", "flag": "--no-witness", "type": "flag", "description": "Suppress witness ledger recording" },
39+
{ "name": "describe", "flag": "--describe", "type": "flag", "description": "Print compiled operator.json and exit 0" },
40+
{ "name": "schema", "flag": "--schema", "type": "flag", "description": "Print canon.v0 JSON Schema and exit 0" },
41+
{ "name": "version", "flag": "--version", "type": "flag", "description": "Print version and exit 0" }
42+
],
43+
44+
"subcommands": [],
45+
46+
"exit_codes": {
47+
"0": { "meaning": "RESOLVED", "domain": "positive" },
48+
"1": { "meaning": "PARTIAL or UNRESOLVED", "domain": "negative" },
49+
"2": { "meaning": "REFUSAL / CLI error", "domain": "error" }
50+
},
51+
52+
"refusals": [
53+
{ "code": "E_IO", "message": "Cannot read file or registry path", "action": "escalate" },
54+
{ "code": "E_ENCODING", "message": "Unsupported text encoding (non-UTF-8)", "action": "escalate" },
55+
{ "code": "E_CSV_PARSE", "message": "CSV header/record parse failure", "action": "escalate" },
56+
{ "code": "E_BAD_REGISTRY", "message": "Invalid registry.json or mapping format", "action": "escalate" },
57+
{ "code": "E_COLUMN_NOT_FOUND", "message": "Column does not exist in header", "action": "fix_input" },
58+
{ "code": "E_PARSE", "message": "Unrecognized file extension or parse error", "action": "escalate" },
59+
{ "code": "E_EMPTY_INPUT", "message": "No processable data rows", "action": "escalate" },
60+
{ "code": "E_TOO_LARGE", "message": "Input exceeds --max-rows or --max-bytes limit", "action": "retry_with_flag", "flag": "--max-rows or --max-bytes" },
61+
{ "code": "E_EMIT_FORMAT", "message": "--emit csv requested with JSONL input", "action": "adjust_input" },
62+
{ "code": "E_COLUMN_EXISTS", "message": "Canonical column name already in CSV header", "action": "adjust_input" }
63+
],
64+
65+
"capabilities": {
66+
"formats": ["csv", "jsonl"],
67+
"profile_aware": false,
68+
"streaming": false,
69+
"witness": {
70+
"ambient_recording": "enabled_by_default",
71+
"query_subcommands": false,
72+
"no_witness_flag": "suppresses_recording"
73+
}
74+
},
75+
76+
"pipeline": {
77+
"upstream": [],
78+
"downstream": ["lock", "pack"]
79+
}
80+
}

src/lib.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,8 @@ pub fn run(cli: Cli) -> Result<u8, Box<dyn Error>> {
2626
}
2727

2828
if cli.describe {
29-
let operator = serde_json::json!({
30-
"tool": "canon",
31-
"version": "0.1.0",
32-
"description": "Canonical identifier resolution tool",
33-
"agent_guide": "https://github.com/cmdrvl/.github/blob/main/profile/AGENT_PROMPT.md",
34-
"schema_version": "canon.v0",
35-
"capabilities": {
36-
"input_formats": ["csv", "jsonl"],
37-
"output_formats": ["json", "csv"],
38-
"hash_algorithm": "BLAKE3"
39-
}
40-
});
41-
println!("{}", serde_json::to_string(&operator)?);
29+
const OPERATOR_JSON: &str = include_str!("../operator.json");
30+
println!("{OPERATOR_JSON}");
4231
return Ok(0);
4332
}
4433

tests/cli_smoke.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ fn test_describe_command() {
2424
let stdout = String::from_utf8(output.get_output().stdout.clone()).unwrap();
2525
let json: Value = serde_json::from_str(&stdout).expect("--describe should output valid JSON");
2626

27-
assert_eq!(json["tool"], "canon");
27+
assert_eq!(json["name"], "canon");
2828
assert_eq!(json["version"], "0.1.0");
29-
assert_eq!(json["schema_version"], "canon.v0");
29+
assert_eq!(json["schema_version"], "operator.v0");
3030
assert!(json["capabilities"].is_object());
3131
}
3232

0 commit comments

Comments
 (0)