feat(describe): bcli describe --format json (AIP Phase 1)#14
Conversation
Implements the canonical agent-facing surface introspection per
agent-cli-contract-v0.1.md §Phase 1.
- New `bcli describe [--format json|table] [<command-path>...]` command
- Projects live Typer app + EndpointRegistry + active BCProfile as one
JSON document — the single source of truth that MCP, completions, and
docs will consume (no parallel schemas).
- Caches at ~/.config/bcli/describe/<profile>.<hash>.json with
registry/profile mtime invalidation; writes are atomic.
- Subtree mode (`bcli describe get`, `bcli describe batch run`) trims
the payload to one command for token-constrained agents — drops
registry + profile_constraints, keeps version/tool/tool_version/profile
for provenance.
- Forward-compat declarations:
* `emits_result_envelope: true` on mutating commands (post/patch/
delete/attach upload/batch run) — Phase 2 attaches to this.
* `emits_operation_state: true` only on `batch run` — Phase 3.
* `requires_confirmation: "production"` on mutating commands.
- Tolerates broken/missing config: when no profile is configured the
command still emits a populated commands list with `profile: null`,
matching the self-rescue posture of `bcli doctor`.
- Additive only — zero behavior change for existing commands.
- Stdlib only, no new runtime deps.
igor-ctrl
left a comment
There was a problem hiding this comment.
Lead review — APPROVE (pending Igor's merge)
Reviewed against agent-cli-contract-v0.1.md §Phase 1 and tasks/todo.md Phase 1 checklist. Worker self-report verified: 650 passed / 5 skipped, ruff clean, additive only, stdlib only.
Schema fidelity
- All top-level keys (
version,tool,tool_version,profile,commands,registry,profile_constraints) match the spec example. commands[].options[].validates: "odata-filter"on--filter— matches the spec sample.commands[].effectstaxonomy (read/mutating/other) —otheris an addition beyond the spec example but a sensible default forauth login,config set, etc.; doesn't break the contract.requires_confirmation: "production"emitted on every mutating verb (spec example only showed it onbatch run; emitting it on all mutating verbs is the conservative reading and aligned with the existing_safety.pyposture).emits_result_envelope: trueon the five mutating paths;emits_operation_state: trueonbatch runonly — exactly the forward-compat declarations Phase 2 / Phase 3 attach to.registry.endpoints[]shape matches;fields_discovered_at: null(deferred; flagged in PR body — additive extension whenEndpointMetadatagains the field).
Implementation quality
- Stdlib only, no new runtime deps.
- Atomic cache write (
tempfile.mkstemp+os.replacewith exception cleanup) — Phase 2 envelope can reuse this pattern. - Lazy imports for
CONFIG_DIR/app— correctly motivated for test monkeypatching and circular-import avoidance; docstring explains it. - Deterministic ordering (commands by path tuple, endpoints by
(tier, entity)) — important for diffing describe outputs across runs; tested. - Broken-install tolerance: nested try/except in
_build_payloadfalls back to a stub registry projection. Matches the self-rescue posture ofbcli doctor. This is the right call for an agent's first command.
Test coverage (19 new tests)
Schema shape, command list invariants, self-reference, sort determinism, mutating flags, no-leak of forward-compat flags onto read commands, batch run operation_state, registry projection with custom endpoints, disable_standard_api reflection, profile constraints (set + default), no-profile tolerance, subtree single match, multipart subtree, unknown-path error path, cache write, cache hit (sentinel proves cache is consulted), cache invalidation on registry mtime, table format. Hits every Phase 1 checklist box.
Live smoke (in tmp worktree)
bcli describe --format json → 46 commands, 122 endpoints, profile resolved
bcli describe get → 1 command, no registry/constraints (token savings)
bcli describe batch run → emits_result_envelope=True, emits_operation_state=True, requires_confirmation=production
Non-blocking observations
_supported_formats_from_signatureparses help text by splitting on:/,— brittle but harmlessly so (falls back to["json"]; existing help strings are consistent)._annotation_to_namedoes string munging on annotations — works for current signatures;list[str]/Union[...]would produce lowercased fragments but won't crash.typer.Exit(4)on unknown subtree path — aligns with the (future) Phase 4 exit-code taxonomy where4 = not found.
After this merges
Branch feat/standardize-cli off main and proceed with Phase 4 per tasks/todo.md.
Verdict: APPROVE. Igor merges; I do not.
Implements AIP v0.1 Phase 1 per
agent-cli-contract-v0.1.md.Summary
bcli describe [--format json|table] [<command-path>...]commandEndpointRegistry+ activeBCProfileas one JSON document — the canonical artifact MCP, completions, and docs all consume (no parallel schemas drifting from the CLI).~/.config/bcli/describe/<profile>.<hash>.jsonwith registry/profile mtime invalidation; writes are atomic (.tmp+os.replace).bcli describe get,bcli describe batch run) — dropsregistry+profile_constraints, keepsversion/tool/tool_version/profilefor provenance.emits_result_envelope: trueon mutating commands (post/patch/delete/attach upload/batch run) — Phase 2 attaches here.emits_operation_state: trueonly onbatch run— Phase 3.requires_confirmation: "production"on mutating commands.profile: null— matches the self-rescue posture ofbcli doctor. Agents calling describe as their first command always get useful output.Design decisions worth flagging
--formatis a local flag ondescribe(not the root--format). Spec example calls it out and Typer happily shadows the root option locally.registryandprofile_constraintsdeliberately — token savings is the whole point. State is documented in the command docstring.(tier, entity)— deterministic output for diffing.fields_discovered_atis included in the per-endpoint shape but emitted asnulluntilEndpointMetadatais extended (deferred — out of scope for this PR).Test plan
uv run pytest tests/test_describe/ -v— 19 tests, all greenuv run pytest tests/ -v— 650 passed, 5 skipped, no regressionsuv run ruff check src/ tests/— cleanbcli describe --format json— 46 commands + 122 registry endpoints projected on real profilebcli describe get --format json— subtree returns single command, drops registry/constraintsbcli describe --format table— Rich table with effects + formats columns