spec-kitty / spec-kitty/spec-kitty

The requirement-extraction advisory is invisible in `spec-kitty next --json` — the documented agent flow

Open
#3,483 0 comments 0 reactions 0 assignees View on GitHub
priority:P2 reliability
Dominant language
Python
Stars
1.6k
Forks
165
Avg merge
14h 52m
Merged PRs (30d)
303

Description

Found by an adversarial review squad during the landing pass on #3395, and confirmed by the implementer who assessed folding it. Deliberately **not** folded — it needs a pinned-contract change, which a landing pass should not make.

Parent epic: #2720 (mission-state discovery & diagnostic-command output fidelity).

## The defect

#3395 added a non-blocking `requirement_extraction_warnings` advisory: it tells an operator that `spec.md` contains requirement-shaped tokens matching none of the recognised declaring shapes, so those requirements are invisible to coverage mapping. On the `spec-kitty next` readiness path the advisory is emitted with `logger.warning`, which is its only channel.

`src/specify_cli/__init__.py:376` installs JSON-mode logging that raises every root handler to `CRITICAL+1` and attaches a `NullHandler` so `lastResort` cannot fire (`logging_bootstrap.py:148-160`).

Measured:

```
json_mode=False stderr = "WARNING [my-mission] "
json_mode=True stderr = ""
```

`docs/guides/how-to/governance/run-governed-mission.md:40` documents the flow as:

```
spec-kitty next --agent claude --mission --json
```

So in the documented agent flow the advisory produces nothing at all, and it is not carried in the decision payload either — `requirement_extraction_warnings` appears nowhere in `runtime_bridge_io.py`. The `spec-kitty next` leg of #3395 is inert for its primary consumer.

The two CLI surfaces (`finalize-tasks`, `map-requirements`) are unaffected — they carry the field in JSON, including on the blocked-operator failure paths as of the #3395 landing folds.

## Why it was not folded into #3395

`Decision` (`src/runtime/next/decision.py`) is explicitly documented as the public JSON contract, with a hand-maintained `to_dict()` and no advisory channel — its only list is `guard_failures`, which is hard-failure-only and must not gain soft entries. Surfacing the advisory means:

1. a new `requirement_extraction_warnings: list[str]` field on `Decision` and `to_dict()`;
2. threading it through `DecisionEnvelope` in `runtime_bridge_cores.py`;
3. the print path in `next_cmd.py`;
4. classifying the new field in the parity-oracle STABLE-field ledger (`tests/runtime/_bridge_oracle.py`).

That is a pinned-contract change across four files. The landing runbook folds squad findings by default but carves out changes needing their own design pass; this is one.

## Acceptance

- `spec-kitty next --json` carries the advisory as plain data, distinct from `guard_failures`, and it never influences a gate verdict or exit code.
- The parity oracle classifies the new field explicitly rather than inheriting a default.
- A test asserts the advisory survives `--json` (the current gap: the only coverage is human-mode logging).

Contributor guide

Open the contributing guide

Research direction

Read src/runtime/next/decision.py and its to_dict(), then trace DecisionEnvelope in runtime_bridge_cores.py and the JSON print path in next_cmd.py. Check tests/runtime/_bridge_oracle.py and the documented next --json flow in docs/guides/how-to/governance/run-governed-mission.md. Done means the advisory appears as separate JSON data, does not affect verdicts or exit codes, is classified by the oracle, and is covered by a test.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.