-
Notifications
You must be signed in to change notification settings - Fork 0
feat: IRONCLAD M9.T1 — CLI JSON Schema contracts #217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bae326a
436af7e
9480b4b
8accc4b
806f0d8
05436f6
d21d5d3
e621e0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| # CLI JSON Contracts | ||
|
|
||
| Every `--json` output from the git-mind CLI includes a standard envelope: | ||
|
|
||
| ```json | ||
| { | ||
| "schemaVersion": 1, | ||
| "command": "status", | ||
| ... | ||
| } | ||
| ``` | ||
|
|
||
| - **`schemaVersion`** — Increments only on breaking changes to the JSON structure. Non-breaking additions (new optional fields) do not increment it. | ||
| - **`command`** — The command that produced this output. Useful for routing in pipelines that handle multiple git-mind outputs. | ||
|
|
||
| ## Version Policy | ||
|
|
||
| | Scenario | schemaVersion change | | ||
| |----------|---------------------| | ||
| | New optional field added | No change | | ||
| | Field renamed or removed | Increment | | ||
| | Field type changed | Increment | | ||
| | Array wrapped in object | Increment | | ||
| | New command added | No change (new schema) | | ||
|
|
||
| ## Command-to-Schema Table | ||
|
|
||
| | Command | Schema File | | ||
| |---------|-------------| | ||
| | `nodes --id <id> --json` | [`node-detail.schema.json`](cli/node-detail.schema.json) | | ||
| | `nodes --json` | [`node-list.schema.json`](cli/node-list.schema.json) | | ||
| | `status --json` | [`status.schema.json`](cli/status.schema.json) | | ||
| | `at <ref> --json` | [`at.schema.json`](cli/at.schema.json) | | ||
| | `import --json` | [`import.schema.json`](cli/import.schema.json) | | ||
| | `import --from-markdown --json` | [`import.schema.json`](cli/import.schema.json) | | ||
| | `export --json` (stdout) | [`export-data.schema.json`](cli/export-data.schema.json) | | ||
| | `export --file <path> --json` | [`export-file.schema.json`](cli/export-file.schema.json) | | ||
| | `merge --json` | [`merge.schema.json`](cli/merge.schema.json) | | ||
| | `doctor --json` | [`doctor.schema.json`](cli/doctor.schema.json) | | ||
| | `suggest --json` | [`suggest.schema.json`](cli/suggest.schema.json) | | ||
| | `review --json` | [`review-list.schema.json`](cli/review-list.schema.json) | | ||
| | `review --batch --json` | [`review-batch.schema.json`](cli/review-batch.schema.json) | | ||
| | `diff <ref-a>..<ref-b> --json` | [`diff.schema.json`](cli/diff.schema.json) | | ||
|
|
||
| > **Note:** `nodes --id` and `nodes` (list) both emit `"command": "nodes"`. To select the correct schema, check for a top-level `id` field (node-detail) vs. a `nodes` array (node-list). | ||
|
|
||
| ## Programmatic Validation | ||
|
|
||
| ```javascript | ||
| import Ajv from 'ajv/dist/2020.js'; | ||
| import schema from './docs/contracts/cli/status.schema.json' with { type: 'json' }; | ||
|
|
||
| const ajv = new Ajv({ strict: true, allErrors: true }); | ||
| const validate = ajv.compile(schema); | ||
|
|
||
| const output = JSON.parse(execSync('git mind status --json')); | ||
| if (!validate(output)) { | ||
| console.error(validate.errors); | ||
| } | ||
| ``` | ||
|
|
||
| ## Migration Guide | ||
|
|
||
| ### `nodes --json` (Breaking) | ||
|
|
||
| The output changed from a bare JSON array to a wrapped object. | ||
|
|
||
| ```bash | ||
| # Before (prior to v3.0.0): | ||
| git mind nodes --json | jq '.[]' | ||
|
|
||
| # After: | ||
| git mind nodes --json | jq '.nodes[]' | ||
| ``` | ||
|
Comment on lines
+68
to
+74
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Verify the "before" version reference. Line 66 and 78 reference "v2.0.0-alpha.5 and earlier", but the previous 🤖 Prompt for AI Agents |
||
|
|
||
| ### `review --json` (Breaking) | ||
|
|
||
| The output changed from a bare JSON array to a wrapped object. | ||
|
|
||
| ```bash | ||
| # Before (prior to v3.0.0): | ||
| git mind review --json | jq '.[].source' | ||
|
|
||
| # After: | ||
| git mind review --json | jq '.pending[].source' | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| { | ||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | ||
| "$id": "https://github.com/neuroglyph/git-mind/docs/contracts/cli/at.schema.json", | ||
| "title": "git-mind at --json", | ||
| "description": "Time-travel output from `git mind at <ref> --json`", | ||
| "type": "object", | ||
| "required": ["schemaVersion", "command", "ref", "sha", "fullSha", "tick", "nearest", "status"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "schemaVersion": { "type": "integer", "const": 1 }, | ||
| "command": { "type": "string", "const": "at" }, | ||
| "ref": { "type": "string" }, | ||
| "sha": { "type": "string", "minLength": 8, "maxLength": 8 }, | ||
| "fullSha": { "type": "string", "pattern": "^[0-9a-f]+$" }, | ||
| "tick": { "type": "integer", "minimum": 0 }, | ||
| "nearest": { "type": "boolean" }, | ||
| "recordedAt": { "type": ["string", "null"] }, | ||
| "status": { | ||
| "type": "object", | ||
| "required": ["nodes", "edges", "health"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "nodes": { | ||
| "type": "object", | ||
| "required": ["total", "byPrefix"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "total": { "type": "integer", "minimum": 0 }, | ||
| "byPrefix": { | ||
| "type": "object", | ||
| "additionalProperties": { "type": "integer", "minimum": 0 } | ||
| } | ||
| } | ||
| }, | ||
| "edges": { | ||
| "type": "object", | ||
| "required": ["total", "byType"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "total": { "type": "integer", "minimum": 0 }, | ||
| "byType": { | ||
| "type": "object", | ||
| "additionalProperties": { "type": "integer", "minimum": 0 } | ||
| } | ||
| } | ||
| }, | ||
| "health": { | ||
| "type": "object", | ||
| "required": ["blockedItems", "lowConfidence", "orphanNodes"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "blockedItems": { "type": "integer", "minimum": 0 }, | ||
| "lowConfidence": { "type": "integer", "minimum": 0 }, | ||
| "orphanNodes": { "type": "integer", "minimum": 0 } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| { | ||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | ||
| "$id": "https://github.com/neuroglyph/git-mind/docs/contracts/cli/diff.schema.json", | ||
| "title": "git-mind diff --json", | ||
| "description": "Graph diff result from `git mind diff <ref-a>..<ref-b> --json`", | ||
| "type": "object", | ||
| "required": ["schemaVersion", "command", "from", "to", "nodes", "edges", "summary", "stats"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "schemaVersion": { "type": "integer", "const": 1 }, | ||
| "command": { "type": "string", "const": "diff" }, | ||
| "from": { "$ref": "#/$defs/diffEndpoint" }, | ||
| "to": { "$ref": "#/$defs/diffEndpoint" }, | ||
| "nodes": { | ||
| "type": "object", | ||
| "required": ["added", "removed", "total"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "added": { "type": "array", "items": { "type": "string" } }, | ||
| "removed": { "type": "array", "items": { "type": "string" } }, | ||
| "total": { | ||
| "type": "object", | ||
| "required": ["before", "after"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "before": { "type": "integer", "minimum": 0 }, | ||
| "after": { "type": "integer", "minimum": 0 } | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| "edges": { | ||
| "type": "object", | ||
| "required": ["added", "removed", "total"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "added": { | ||
| "type": "array", | ||
| "items": { "$ref": "#/$defs/edgeDiffEntry" } | ||
| }, | ||
| "removed": { | ||
| "type": "array", | ||
| "items": { "$ref": "#/$defs/edgeDiffEntry" } | ||
| }, | ||
| "total": { | ||
| "type": "object", | ||
| "required": ["before", "after"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "before": { "type": "integer", "minimum": 0 }, | ||
| "after": { "type": "integer", "minimum": 0 } | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| "summary": { | ||
| "type": "object", | ||
| "required": ["nodesByPrefix", "edgesByType"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "nodesByPrefix": { | ||
| "type": "object", | ||
| "additionalProperties": { | ||
| "type": "object", | ||
| "required": ["before", "after"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "before": { "type": "integer", "minimum": 0 }, | ||
| "after": { "type": "integer", "minimum": 0 } | ||
| } | ||
| } | ||
| }, | ||
| "edgesByType": { | ||
| "type": "object", | ||
| "additionalProperties": { | ||
| "type": "object", | ||
| "required": ["before", "after"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "before": { "type": "integer", "minimum": 0 }, | ||
| "after": { "type": "integer", "minimum": 0 } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| "stats": { | ||
| "type": "object", | ||
| "required": ["materializeMs", "diffMs", "nodeCount", "edgeCount"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "sameTick": { "type": "boolean" }, | ||
| "materializeMs": { | ||
| "type": "object", | ||
| "required": ["a", "b"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "a": { "type": "integer", "minimum": 0 }, | ||
| "b": { "type": "integer", "minimum": 0 } | ||
| } | ||
| }, | ||
| "diffMs": { "type": "integer", "minimum": 0 }, | ||
| "nodeCount": { | ||
| "type": "object", | ||
| "required": ["a", "b"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "a": { "type": "integer", "minimum": 0 }, | ||
| "b": { "type": "integer", "minimum": 0 } | ||
| } | ||
| }, | ||
| "edgeCount": { | ||
| "type": "object", | ||
| "required": ["a", "b"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "a": { "type": "integer", "minimum": 0 }, | ||
| "b": { "type": "integer", "minimum": 0 } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| "$defs": { | ||
| "diffEndpoint": { | ||
| "type": "object", | ||
| "required": ["ref", "sha", "tick", "nearest"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "ref": { "type": "string" }, | ||
| "sha": { "type": "string", "minLength": 8, "maxLength": 8 }, | ||
| "tick": { "type": "integer", "minimum": 0 }, | ||
| "nearest": { "type": "boolean" } | ||
| } | ||
| }, | ||
| "edgeDiffEntry": { | ||
| "type": "object", | ||
| "required": ["source", "target", "type"], | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "source": { "type": "string", "minLength": 1 }, | ||
| "target": { "type": "string", "minLength": 1 }, | ||
| "type": { "type": "string" } | ||
| } | ||
| } | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.