Skip to content

feat(check): emit the check result as a machine-readable document - #21

Merged
jdh313 merged 1 commit into
mainfrom
check-json-output
Aug 29, 2026
Merged

feat(check): emit the check result as a machine-readable document#21
jdh313 merged 1 commit into
mainfrom
check-json-output

Conversation

@jdh313

@jdh313 jdh313 commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Adds --json to check, emitting one JSON document on stdout instead of the human lines.

$ agentforge check MARKETPLACE.yaml --out marketplaces --json
{
  "schemaVersion": 1,
  "marketplaceId": "jdh-agents",
  "status": "ok",
  "publications": [
    { "id": "claude", "status": "ok", "filesChecked": 213 },
    { "id": "codex",  "status": "ok", "filesChecked": 207 }
  ],
  "issues": [],
  "diagnostics": [ … ]
}

Why

compile already had --report with a JSON format; check had nothing. A consuming repository parses the diagnostic stream back apart — a regex over merged stdout and stderr, then re-deriving paths by string-matching the literal <root> token the formatter emits for root-anchored manifests.

That makes the consumer one formatting change away from silently finding zero issues. A gate that fails open is the wrong direction for a gate to fail, and this removes the parsing layer rather than asking every consumer to maintain one.

Design calls, each against a standing decision

  • schemaVersion: 1ndr:r51yhr binds "the JSON compilation report, and any later machine-targeted format." This is one.
  • No disposition grouping, though ndr:71jgk2 binds "every report format." Check codes are already dispositional — missing-output, changed-output, unsafe-output-content each name what became of the thing — and check issues carry no severity, so there is no axis to reorganise. Imposing the grouping would add a layer that classifies nothing. The reasoning is in a code comment so it is not re-litigated later.
  • status hoisted to the top level, so a consumer branches on it rather than on issues.length. Reading "no issues parsed" as "passed" is precisely the failure mode the text format invited; a test pins it.
  • --json owns stdout entirely — one stray human line makes the document unparseable. --claude-native output still writes through, since suppressing another tool's output would hide why a run failed.

Testing

  • 170 pass / 0 fail; biome and typecheck clean.
  • 3 new tests: clean report shape, failed status with issues present, and diagnostics with provenance flattened.
  • Run against the real jdh-agents corpus: valid JSON, 67,895 bytes, and filesChecked (213 / 207) matches the text path exactly, so the two views cannot disagree.

Also adds "./report" to the package exports map — it had no entry, so tests could not import the builder.

Risk surface

Low and additive. The default (no --json) path is byte-identical; exit codes are unchanged in both modes. The one shared-code change is buildCheckReport reading MarketplaceCheckResult, which it does not mutate.

Note this is a feat:, so merging cuts 0.4.0 — and it is the first release since the npm job landed, so it will be the first real exercise of publish-npm. That job has never run; if it fails, the release assets are already attached by then and only the npm half is affected.

Rollback

Revert the commit. --json disappears; the text path and every exit code are untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LxNdWk6ZUzq6uWFtd94yWD

Add `--json` to `check`, emitting one JSON document on stdout instead of the
human lines. `compile` already had `--report`; `check` had nothing, so a
consuming repository parsed the diagnostic stream back apart with a regex over
merged stdout and stderr, and re-derived paths by string-matching the `<root>`
token the formatter emits. A gate whose consumer is one formatting change away
from silently finding zero issues fails open, which is the wrong direction.

The document carries `schemaVersion`, since ndr:r51yhr binds any machine-
targeted format. `status` is hoisted to the top level so a consumer branches on
it rather than on an empty `issues` array — reading "no issues parsed" as
"passed" is the failure the text format invited, and a test pins it.

It deliberately does not group by disposition, though ndr:71jgk2 binds every
report format. Check codes are already dispositional — missing-output,
changed-output, unsafe-output-content each name what became of the thing — and
there is no severity axis to reorganise, so the grouping would classify nothing.

Also exports ./report from package.json; it had no entry, so tests could not
import the builder.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LxNdWk6ZUzq6uWFtd94yWD
@jdh313
jdh313 merged commit ea562dc into main Aug 29, 2026
1 check passed
@jdh313
jdh313 deleted the check-json-output branch August 29, 2026 18:25
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 0.4.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant