Contracts

validate-json-output.md

Contract: charter bundle validate --json Output Shape

Decision: DM-01KQFAPRVNBB7V1QWN1E4C2VJ7 — Nested synthesis_state key + mirror errors into top-level errors list.


Existing bundle fields retained

{
  "result": "success",
  "canonical_root": "/path/to/repo",
  "manifest_schema_version": "1.0.0",
  "bundle_compliant": true,
  "passed": true,
  "errors": [],
  "tracked_files": {
    "expected": [".kittify/charter/charter.md"],
    "present": [".kittify/charter/charter.md"],
    "missing": []
  },
  "derived_files": {
    "expected": [".kittify/charter/governance.yaml"],
    "present": [".kittify/charter/governance.yaml"],
    "missing": []
  },
  "gitignore": {
    "expected_entries": [".kittify/charter/governance.yaml"],
    "present_entries": [".kittify/charter/governance.yaml"],
    "missing_entries": []
  },
  "out_of_scope_files": [],
  "warnings": [],
  "synthesis_state": {
    "present": false,
    "passed": true,
    "errors": [],
    "warnings": []
  }
}

Post-mission shape — synthesis failure

{
  "passed": false,
  "errors": [
    "synthesis_state: missing provenance sidecar for 'directives/001-example.directive.yaml'"
  ],
  "tracked_files": { "...": "existing tracked_files section retained" },
  "derived_files": { "...": "existing derived_files section retained" },
  "gitignore": { "...": "existing gitignore section retained" },
  "out_of_scope_files": [],
  "warnings": [],
  "synthesis_state": {
    "present": true,
    "passed": false,
    "errors": [
      "missing provenance sidecar for 'directives/001-example.directive.yaml'"
    ],
    "warnings": []
  }
}

Post-mission shape — legacy bundle (no synthesis state)

{
  "passed": true,
  "errors": [],
  "tracked_files": { "...": "existing tracked_files section retained" },
  "derived_files": { "...": "existing derived_files section retained" },
  "gitignore": { "...": "existing gitignore section retained" },
  "out_of_scope_files": [],
  "warnings": [],
  "synthesis_state": {
    "present": false,
    "passed": true,
    "errors": [],
    "warnings": []
  }
}

Post-mission shape — complete v2 bundle

{
  "passed": true,
  "errors": [],
  "tracked_files": { "...": "existing tracked_files section retained" },
  "derived_files": { "...": "existing derived_files section retained" },
  "gitignore": { "...": "existing gitignore section retained" },
  "out_of_scope_files": [],
  "warnings": [],
  "synthesis_state": {
    "present": true,
    "passed": true,
    "errors": [],
    "warnings": []
  }
}

Rules

1. synthesis_state is always present in the JSON output (all paths: pass, fail, legacy). 2. synthesis_state.present is false when no synthesis state exists on disk; true otherwise. 3. synthesis_state.errors contains undecorated error strings from BundleValidationResult.errors. 4. Each entry in synthesis_state.errors is also mirrored into the top-level errors list, prefixed with "synthesis_state: ". 5. synthesis_state.warnings contains non-blocking warnings from BundleValidationResult.warnings. Warnings are NOT mirrored into the top-level errors list. 6. Top-level passed is false if either the legacy manifest checks or synthesis_state.passed is false. 7. No Rich/plain text may appear on stdout before or after this JSON envelope when --json is active. Rich console output goes to stderr in --json mode. 8. Error strings in synthesis_state.errors must identify the specific artifact path or field that caused the failure — not just a generic category.