Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .specify/feature.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"feature_directory": "specs/052-conditional-landing"
"feature_directory": "specs/053-assumption-review-console"
}
79 changes: 74 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -543,13 +543,14 @@ Beads-only workflow model. All development is driven by beads (`bd` CLI).
| `maverick refuel <plan-name>` | Decompose plan into beads |
| `maverick refuel <feature> --speckit [--dry-run\|--enrich]` | Deterministic Spec Kit ingestion |
| `maverick fly --epic <id>` | Implement beads (actor-mailbox) |
| `maverick land [--eject\|--finalize]` | Curate history and merge |
| `maverick reconcile [--dry-run]` | Reapply changed human answers into jj history |
| `maverick land [--eject\|--finalize] [--status] [--json]` | Curate history and merge, or query the frontier |
| `maverick reconcile [--dry-run] [--json]` | Reapply changed human answers into jj history |
| `maverick workspace status\|clean` | Manage hidden workspace |
| `maverick init` | Initialize a Maverick project |
| `maverick init` | Initialize a Maverick project (installs the `maverick-review` skill) |
| `maverick brief [--watch\|--human]` | Bead status + assumption counts |
| `maverick review <bead-id> [--answer\|--waive]` | Resolve a human-assigned bead |
| `maverick review --spec <name> --waive <reason>` | Bulk-waive a spec's open entries |
| `maverick review <bead-id> [--answer\|--waive] [--json]` | Resolve a human-assigned bead |
| `maverick review --spec <name> --waive <reason> [--json]` | Bulk-waive a spec's open entries |
| `maverick review --list [--status\|--spec\|--severity]... [--json]` | List ledger entries with full provenance |
| `maverick runway seed\|consolidate` | Manage knowledge store |

### spec (headless Spec Kit chain)
Expand Down Expand Up @@ -654,6 +655,28 @@ owns matching the severity filter (default: low only) in one
invocation — the strict any-severity gate makes clearing accepted-risk
low-severity noise a named operation rather than one-by-one.

**JSON verbs (053-assumption-review-console)**: `land --status --json`
runs the gate evaluation and builds/persists the report without
curating — a read-only frontier query (`verb: land.status`, always
exits 0 on a completed evaluation, blocked is an answer not a failure).
`land --json` (with `--yes` to skip the interactive curation-approval
prompt, which is unreachable in JSON mode) emits the same envelope
shape as every other JSON verb; a frontier refusal is `ok: false`,
`kind: frontier-blocked`, with the full report under
`error.details.report`. Both documents carry a top-level `degraded`
flag: when the ledger can't be read at all the gate degrades *open*
and materializes zero entries, so `frontier_clear` is trivially true —
consumers must read `frontier_clear && !degraded` as the landable
condition. See
`specs/053-assumption-review-console/contracts/cli-land-json.md` and
`error-envelope.md` for the full contract; `src/maverick/cli/json_output.py`
is the shared envelope/error-kind implementation every JSON verb uses.

The gate evaluation, report building, terminal rendering, and
persistence shared by `land` and `land --status` live in
`src/maverick/cli/commands/land_gate.py` — both commands import it,
neither imports the other.

### reconcile

`maverick reconcile [--dry-run]` retroactively reapplies human answers to
Expand Down Expand Up @@ -696,6 +719,20 @@ via `maverick review` re-arms it for the next reconcile run (FR-017).
See `src/maverick/workflows/reconcile/` and
`specs/051-reconcile-changed-answers/` for the full contract.

**JSON verbs (053-assumption-review-console)**: `reconcile --json`
(verb `reconcile.run`) and `reconcile --dry-run --json` (verb
`reconcile.dry-run`) emit `ReconcileReport.to_dict()` under `result`
with workflow progress routed to stderr; precondition failures
(dirty working copy, concurrent fly run, held lockfile, missing
`.jj`) map to the `dirty-working-copy`/`concurrent-run`/`locked`/`vcs`
error kinds instead of a bare stderr message. That mapping keys off
`WorkflowError.reason` — a stable code (`maverick.exceptions.workflow`'s
`REASON_*` constants) the raiser sets, so rewording a precondition
message can't silently reclassify it as `internal`; prose-marker
matching survives only as a fallback for raisers that set no `reason`.
Exit-code semantics are unchanged from the non-JSON contract. See
`specs/053-assumption-review-console/contracts/cli-reconcile-json.md`.

**Mid-flight integration (052-conditional-landing)**: a running
`maverick fly` triggers this same workflow in-process at every bead
boundary (`### fly` above) when detection is non-empty, passing
Expand Down Expand Up @@ -740,6 +777,38 @@ clears any prior reconcile status on re-answer so a corrected human
answer re-enters reconcile detection without special-casing elsewhere.
See `### reconcile` above and `specs/051-reconcile-changed-answers/`.

### Assumption review console (053-assumption-review-console)

Every review-lifecycle verb is invocable headlessly with `--json`:
`review --list [--status\|--spec\|--severity]...` (verb `review.list`,
full provenance rows filtered/sorted server-side — owning spec asc,
severity high→low, stable ledger order), `review <id> --answer/--waive`
(verbs `review.answer`/`review.waive`, with an already-resolved
pre-check that applies in both JSON and human mode), and
`review --spec <name> --waive <reason>` (verb `review.bulk-waive`). All
JSON verbs across `review`/`reconcile`/`land` share one envelope shape
and error-kind registry (`src/maverick/cli/json_output.py`,
`specs/053-assumption-review-console/contracts/error-envelope.md`); the
canonical entry-row projection (`src/maverick/assumptions/serialize.py`
`entry_to_dict`) is shared verbatim by `review --list` and the land
report so both surfaces can never drift. `maverick review` without
`--json` is unchanged (FR-018) — it remains the bare-terminal fallback
for humans without Claude Code.

`maverick init` installs a packaged Claude Code skill,
`maverick-review` (`src/maverick/skills/review_console/SKILL.md`,
installed to `<project>/.claude/skills/maverick-review/SKILL.md`,
always overwritten — Maverick-owned, versions with the wheel; removed
by `maverick uninstall`). Invoked as `/maverick-review` or by prose, it
sweeps the open queue one entry at a time via `AskUserQuestion`
(adopted answer + alternatives + free-form + waive/skip), applies each
decision immediately through the JSON verbs above, then — once, after
the sweep — runs `reconcile --json`, reports the frontier via
`land --status --json`, and offers to land only on explicit
confirmation. The skill never touches jj/git/bd or files directly; see
`specs/053-assumption-review-console/contracts/skill-review-console.md`
for its full behavioral contract.

## Dependencies

- [uv](https://docs.astral.sh/uv/) for dependencies (`uv sync`).
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ packages = ["src/maverick"]
include = [
"src/maverick/**/*.py",
"src/maverick/agents/system_prompts/*.md",
"src/maverick/skills/**/*.md",
]

[tool.ruff]
Expand Down
23 changes: 21 additions & 2 deletions specs/052-conditional-landing/contracts/land-report-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ and carries no independent fields.
"entries": [
{
"bead_id": "ma-0042",
"owner_spec": "052-conditional-landing",
"status": "open | answered | waived",
"bucket": "resolved | waived | open",
"blocks_landing": false,
"question": "…",
"adopted_answer": "…",
"final_answer": "… | null",
Expand All @@ -35,13 +38,13 @@ and carries no independent fields.
"affected_change_ids": ["zzkw…", "rlvk…"],
"waiver": { "by": "…", "at": "2026-07-24T14:00:00Z", "reason": "…" },
"reconcile": {
"status": "reconciled | needs-interactive-review | skipped | pending | null",
"status": "reconciled | needs-interactive-review | pending | null",
"reconciled_answer": "… | null",
"change_id": "… | null",
"reason": "… | null"
},
"pending_reconcile": false,
"annotations": ["legacy", "reconcile: skipped"]
"annotations": ["legacy", "reconcile: needs-interactive-review"]
}
]
}
Expand All @@ -61,6 +64,22 @@ Field notes:
(report may be partial; `verification` omitted → key absent).
- `verification: "blocked"` reports never accompany a landing — they are the
audit trail of a refused attempt.
- `reconcile.status` never actually persists as `"skipped"` — the ledger
only ever writes `"reconciled"` or `"needs-interactive-review"` as
terminal values, plus the non-terminal `"pending"` re-arm sentinel (a
reconcile run that skips an entry, e.g. an immutable target, still
terminal-marks it `"needs-interactive-review"`, per
`specs/051-reconcile-changed-answers/`); this corrects an earlier,
inaccurate version of this schema note that listed `"skipped"` as a
possible persisted value.
- `owner_spec`, `status` (the ledger's `open|answered|waived` lifecycle
state, distinct from `bucket`'s land-report-specific grouping), and
`blocks_landing` are additive fields (053-assumption-review-console) —
present on every row from that feature onward, needed by
`review --list --json`'s flat listing (which has no section key to
carry `owner_spec` the way this report's `specs[].owner_spec` does) and
reused here for schema parity between the two surfaces. `schema_version`
is unchanged (still `1`) since additive fields don't require a bump.

## `land-report.md` (structure, not schema)

Expand Down
43 changes: 43 additions & 0 deletions specs/053-assumption-review-console/checklists/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Specification Quality Checklist: Assumption Review Console

**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-07-25
**Feature**: [spec.md](../spec.md)

## Content Quality

- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed

## Requirement Completeness

- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified

## Feature Readiness

- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification

## Notes

- "Claude Code skill" and "machine-readable output mode of existing commands"
are named deliberately: they are the product surface the user requested, not
leaked implementation detail. Mechanism-level choices (question-presentation
tooling, output serialization format, flag names) are left to planning.
- The one-batched-reconcile rule, the immediate per-decision verb application,
and the no-bypass land gate are the load-bearing behavioral constraints;
each is covered by an FR, an acceptance scenario, and a success criterion.
- No [NEEDS CLARIFICATION] markers were required — the feature description was
unusually complete; remaining defaults (skip semantics, bulk-waive severity
default, landing mode) are documented in Assumptions.
120 changes: 120 additions & 0 deletions specs/053-assumption-review-console/contracts/cli-land-json.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Contract: `maverick land` JSON modes

**Feature**: 053-assumption-review-console
**Envelope**: all documents follow `error-envelope.md`.
Human-mode behavior (no `--json`) and the 052 contracts
(`specs/052-conditional-landing/contracts/cli-land.md`,
`land-report-schema.md`) are unchanged. The `report` object embedded
below is exactly `LandReport.to_dict()` (schema_version 1, additive-only
per 052); this feature adds `owner_spec`, `status`, `bucket`, and
`blocks_landing` keys to its entry rows (additive, legal under 052).

## `land --status [--json]` — verb `land.status`

Read-only frontier/landability query. Evaluates the assumption gate,
builds **and persists** the land report (`.maverick/runs/<run-id>/
land-report.{json,md}` — persistence failure degrades to a stderr
warning and `"degraded_persistence": true`, never a failure), and stops.
No curation, no consolidation, no manifest display, no history mutation.

- `--status` is mutually exclusive with `--dry-run`, `--eject`,
`--finalize`, `--no-curate`, `--heuristic-only`, `--yes`. `--base` and
`--branch` are accepted but ignored in `--status` mode (`--base` has a
default and is therefore always present; the gate evaluation does not
use it).

### Result

```json
{
"degraded": false,
"frontier_clear": false,
"verification": "verified | conditionally-verified | blocked | null",
"blocking": {
"open": ["mav-101", "mav-102"],
"pending_reconcile": ["mav-201"]
},
"report": { LandReport },
"report_paths": {"json": ".maverick/runs/<id>/land-report.json",
"md": ".maverick/runs/<id>/land-report.md"}
}
```

- **`degraded: true` means the ledger could not be read at all** (bd
unavailable, or the query failed). The gate degrades *open* on that
path, so it materializes zero entries and `frontier_clear` is
trivially `true` — a degraded document is therefore
indistinguishable from a genuinely verified one on `frontier_clear`
alone. **Consumers MUST treat `frontier_clear && !degraded` as the
landable condition.** Distinct from `degraded_persistence`, which
only says the report artifact couldn't be *written*.
- `verification` is `null` exactly when `degraded` is `true` —
mirrors 052's omitted-verification semantics; `degraded` inside
`report` carries the same signal nested.
- `blocking` lists are empty when `frontier_clear` is true.

### Exit codes

- `0` — always, on any completed evaluation (blocked is an **answer**,
not a failure, for a status query).
- `1` — only for error envelopes (`internal`, `vcs`).

## `land [--json] [--yes] [--dry-run] [--no-curate|--heuristic-only] [--eject|--finalize] [--base <rev>] [--branch <name>]` — verb `land.run`

### Behavior

- **Gate refusal**: frontier non-empty → `ok: false`, `kind:
frontier-blocked`, `error.details.report` = full report document,
exit 1. Same gate as human mode; `--json` introduces no bypass
(FR-007). Exception: with `--dry-run`, the full preview still runs and
the document reports the block; exit is deferred to the end (052
behavior preserved).
- **Consent**: the agent-curation approval prompt never fires in JSON
mode. Reaching it without `--yes` → `confirmation-required` envelope,
exit 1, before any plan execution. `--yes`, `--no-curate`,
`--heuristic-only`, and `--dry-run` paths need no prompt. Consent is
the caller's job (the skill asks the human first).
- Progress and curation narration go to stderr.
- "Nothing to land" is success: `ok: true`, `"landed": false`,
`"reason": "nothing-to-land"`. It carries the **same key set** as
every other `land.run` success document (so a consumer reading
`verification` / `report` / `curation` never `KeyError`s), with
`"report": null` — the gate is not evaluated on this path, since
there is no landing to gate.

### Result

```json
{
"landed": true,
"mode": "approve | eject | finalize | dry-run",
"verification": "verified | conditionally-verified | null",
"degraded": false,
"curation": {
"strategy": "none | heuristic | agent",
"executed_count": 3,
"total_count": 3
},
"report": { LandReport },
"report_paths": { "json": "...", "md": "..." },
"hint": "mode-specific next-step text | null"
}
```

- `degraded` carries the same meaning as in `land.status` above: the
ledger could not be read, so `verification` is `null` and the gate
passed vacuously rather than on a clean frontier.
- `curation.executed_count` / `total_count` are curation-operation
counts on the `agent` strategy and squash counts on `heuristic`.
The heuristic strategy additionally carries `absorb_ran` (bool) and
`squashed_count` (int): `jj absorb` rewrites history without
squashing anything, so `squashed_count: 0` alone cannot distinguish
"absorb folded N fixups" from "nothing to do".

### Exit codes

- `0` — landed (or dry-run preview with clear frontier; or
nothing-to-land).
- `1` — `frontier-blocked`, `confirmation-required`, `curation-failed`,
or any other error envelope; dry-run with blocked frontier (deferred,
052 semantics).
Loading