Summary
project-standards reconcile --check --json reports "ok": true while exiting 1 on a plan with pending mutations. The top-level ok and drift fields that reconcile --json emits are not documented, and ok does not carry the same meaning it carries for init --migrate --json, where docs/usage.md explicitly promises it does.
Context
- Adoption mode: fresh adoption (Catalog 5, no prior standards authority)
- Project Standards version:
5.29.0, installed from git+https://github.com/L3DigitalNet/project-standards@v5.29.0
- Selected packages:
adr (latest -> 1.6), agent-handoff (latest -> 1.17)
- Relevant options:
adr.config.require_sections = true; agent-handoff.config = { contract_version = "1.1", startup = "automatic", harnesses = ["claude-code", "codex"] }
- OS / Python: Linux x86_64, Python 3.14.7, installed with
uv tool install
- Harness: an agent following
docs/adoption-prompt-blank.md
Reproduction
project-standards init --catalog 5
project-standards standards enable adr --version latest
project-standards standards enable agent-handoff --version latest
# add the two [standards.*.config] blocks from the adoption guides
project-standards reconcile --check --json
Expected
Either the ok field tracks readiness the way the documented init --migrate --json contract does, or docs/usage.md documents what ok and drift mean for reconcile --json so a consumer can tell which field is the readiness signal.
Actual
Exit code 1, with:
{ "ok": true, "drift": true, "mode": "check", "plan": { "findings": [] } }
findings is empty. After reconcile --apply, the same command returns exit 0 with {"ok": true, "drift": false}.
Why this is confusing
docs/usage.md documents the two commands differently:
- For
init --migrate: "the JSON ok and applicable fields carry the same readiness signal as the exit code."
- For
reconcile: the --json option is described only as emitting "stable plan, action, finding, recovery, or apply fields". Neither ok nor drift is defined, and the exit-status line ("1 drift/findings/apply failure") is the only readiness contract.
So the same field name means "ready" in one command and "no findings" in the other. That difference is what issue #23 fixed for init --migrate; the same reading trap remains for reconcile.
Consumer consequence
Non-blocking, but it costs review time and invites a wrong automation. An agent or CI step that learned ok == readiness from the init --migrate contract will read a pending, unapplied reconcile as complete and skip the apply. The official agent adoption prompt has to coach around this explicitly — it tells the operator to "verify that JSON still reports ok: true" while treating --check drift as expected — which is evidence that the field pair needs a written contract rather than per-prompt guidance.
Suggested resolution
Document the reconcile --json top-level fields in docs/usage.md under reconcile, stating explicitly that ok reports the absence of findings/conflicts and drift reports pending mutations, and that the exit code tracks drift || !ok. Cross-link #23 so the two contracts read together.
Workaround
Read drift (or the exit code) rather than ok when deciding whether an apply is still pending. No functional impact; the tool behaved correctly throughout the adoption.
Related: #23.
Summary
project-standards reconcile --check --jsonreports"ok": truewhile exiting1on a plan with pending mutations. The top-levelokanddriftfields thatreconcile --jsonemits are not documented, andokdoes not carry the same meaning it carries forinit --migrate --json, wheredocs/usage.mdexplicitly promises it does.Context
5.29.0, installed fromgit+https://github.com/L3DigitalNet/project-standards@v5.29.0adr(latest->1.6),agent-handoff(latest->1.17)adr.config.require_sections = true;agent-handoff.config = { contract_version = "1.1", startup = "automatic", harnesses = ["claude-code", "codex"] }uv tool installdocs/adoption-prompt-blank.mdReproduction
Expected
Either the
okfield tracks readiness the way the documentedinit --migrate --jsoncontract does, ordocs/usage.mddocuments whatokanddriftmean forreconcile --jsonso a consumer can tell which field is the readiness signal.Actual
Exit code
1, with:{ "ok": true, "drift": true, "mode": "check", "plan": { "findings": [] } }findingsis empty. Afterreconcile --apply, the same command returns exit0with{"ok": true, "drift": false}.Why this is confusing
docs/usage.mddocuments the two commands differently:init --migrate: "the JSONokandapplicablefields carry the same readiness signal as the exit code."reconcile: the--jsonoption is described only as emitting "stable plan, action, finding, recovery, or apply fields". Neitheroknordriftis defined, and the exit-status line ("1drift/findings/apply failure") is the only readiness contract.So the same field name means "ready" in one command and "no findings" in the other. That difference is what issue #23 fixed for
init --migrate; the same reading trap remains forreconcile.Consumer consequence
Non-blocking, but it costs review time and invites a wrong automation. An agent or CI step that learned
ok == readinessfrom theinit --migratecontract will read a pending, unapplied reconcile as complete and skip the apply. The official agent adoption prompt has to coach around this explicitly — it tells the operator to "verify that JSON still reportsok: true" while treating--checkdrift as expected — which is evidence that the field pair needs a written contract rather than per-prompt guidance.Suggested resolution
Document the
reconcile --jsontop-level fields indocs/usage.mdunderreconcile, stating explicitly thatokreports the absence of findings/conflicts anddriftreports pending mutations, and that the exit code tracksdrift || !ok. Cross-link #23 so the two contracts read together.Workaround
Read
drift(or the exit code) rather thanokwhen deciding whether an apply is still pending. No functional impact; the tool behaved correctly throughout the adoption.Related: #23.