Skip to content

Require a Tracking Value on an investigate Divergence Disposition - #1556

Merged
ptr727 merged 1 commit into
developfrom
feature/669-require-tracking-on-investigate
Sep 12, 2026
Merged

Require a Tracking Value on an investigate Divergence Disposition#1556
ptr727 merged 1 commit into
developfrom
feature/669-require-tracking-on-investigate

Conversation

@ptr727

@ptr727 ptr727 commented Sep 12, 2026

Copy link
Copy Markdown
Owner

What this changes

investigate is the one disposition in the divergence ledger's vocabulary that names no outcome. The other five each state what happens to the divergence, where this one states only that nobody has decided yet, so an entry carrying it beside a null tracking recorded that a decision was owed while naming nowhere it was being made, and the deferral read as an omission.

This makes the requirement structural rather than a one-time ledger edit:

  • spec/divergences.schema.json gains a $defs conditional requiring a non-empty tracking on an investigate entry, applied to the dispositions and gaps arrays alike, and both tracking declarations point at it.
  • spec/validate.py gains the same check, as a pure investigate_tracking_errors helper wired into both loops, so CI refuses such an entry rather than an editor alone.
  • The field stays optional under every other disposition, each of which already names its own outcome, and a test case holds that scoping.
  • spec/divergences.json's own note and README.md's ledger sentence both record the asymmetry beside the shape they describe.

Why a ledger edit would not have closed #669

The issue names three entries. Two left the investigate set already, settled in #736, and the third, pyproject.toml, already carried a tracking value. Re-applying that edit would have closed nothing: the issue's actual subject, that investigate schedules no decision and records no owner, regenerates the moment anyone writes the next entry. Hence the rule rather than the row.

The one row moved to a successor issue

That row previously tracked #669 itself. Closing #669 as fixed by this change would have left the fleet's only governed row pointing at a closed issue where nothing was being decided, which is the state #669 was filed about, with both new gates reading clean over it. The still-owed decision, whether the Python repos carry an equivalent config-only pyproject.toml, is now #1553, and the row's tracking and its reason point there.

The schema and the gate are deliberately unequal, in one direction

The schema refuses an absent, null, empty, or non-string value. The gate refuses every one of those plus a value that is only whitespace. Closing that last gap with a schema pattern would refuse a value the gate accepts, which is the direction #1504 reverted for the registry schema, for this same reason: U+FEFF is an ECMA-262 \s character and str.strip does not remove it, so a \S pattern would refuse a value this gate keeps. So the schema carries a description naming the gate's extra rule, and InvestigateTrackingSchemaMirrorCase pins both halves and that direction, in the shape RegistrySchemaMirrorCase already established for the advisory registry schema. Verified with a real validator: the schema is nowhere stricter than the gate.

Verification

  • The schema conditional is machine-checked under jsonschema 4.26.0, reached through uvx since it is not installed: the live ledger validates clean, and null, absent, and empty tracking are each rejected. Switching the disposition to accepted with null tracking validates clean, so the requirement does not leak onto the five dispositions that name an outcome.
  • The validator wiring is proved by a positive control rather than by a green run, since a compliant ledger only exercises the accept path. Nulling the tracking value in a scratch tree produced exactly the new error, 20 errors against the baseline's 19, and that message appears nowhere in the baseline output.
  • InvestigateTrackingWiringCase runs the real script against a scratch ledger deferring in both arrays, with a marker defect per array proving each loop was reached. Mutation-checked: deleting either call site, swapping the dispositions label to the gap form, weakening the .strip(), removing the gaps conditional, and relaxing minLength to 0 each fail the new tests.
  • Gates green on the final content: ruff format --check, ruff check, mypy, spec/validate.py, 1400 self-tests, spec/audit.py --selftest, spec/workflow_reuse.py --selftest, the prose gate whole-tree, the EOL check, and build_dist --check. canonical_review.py reports no carried canonical unit changed.

Three findings not fixed here, with reasons

  • The check is presence-only, so a string such as TODO satisfies it. The maintainer's decision was that an investigate entry gains a tracking value, and a strict issue-shaped pattern would reject legitimate tracker forms, so the error message names the expected form and the second legitimate remedy instead: where no decision is pending, the fix is the disposition that names the outcome.
  • A non-string truthy tracking, such as 42, reports two lines, the pre-existing type error and this one. Both are true and neither shadows the other, so this is report noise rather than a wrong answer.
  • The generated reports/divergences.md still renders #669 for that row. It is regenerated rather than hand-edited, and regenerating joins against live fleet reality over the network, so it would pull unrelated fleet drift into this diff. That report already lagged the ledger independently, on the .markdownlint-cli2.jsonc row since a37e8a8. Both rows are tracked in Regenerate reports/divergences.md, whose tracking values lag the ledger in two rows #1554.

Refs #669

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Clarified that entries marked investigate must include a tracking value identifying where the decision is being made.
    • Documented that this requirement applies to both dispositions and gaps.
  • Validation

    • Added validation to reject investigate entries with missing, empty, or whitespace-only tracking values.
    • Updated the schema to conditionally require tracking information for investigate entries while preserving existing tracking type rules.

`investigate` is the one disposition in the divergence ledger's vocabulary that
names no outcome. The other five each state what happens to the divergence, where
this one states only that nobody has decided yet, so an entry carrying it beside a
null `tracking` recorded that a decision was owed while naming nowhere it was
being made, and the deferral then read as an omission.

Make the requirement structural rather than a one-time ledger edit. The schema
gains a conditional requiring a non-empty `tracking` on an `investigate` entry, in
the `dispositions` and `gaps` arrays alike, and `spec/validate.py` gains the same
check so CI refuses one rather than an editor alone. The field stays optional under
every other disposition, each of which already names its own outcome, and a test
case holds that scoping. The ledger's own note records the asymmetry beside the
vocabulary it belongs to, and `README.md` records it beside the entry shape.

The schema and the gate are deliberately unequal, and in one direction only. The
schema refuses an absent, null, empty, or non-string value, and the gate refuses
every one of those plus a value that is only whitespace. Closing that last gap with
a schema pattern would refuse a value the gate accepts, which is the direction
#1504 reverted for the registry schema, for this same reason: U+FEFF is an
ECMA-262 `\s` character and `str.strip` does not remove it, so a `\S` pattern would refuse a value this gate keeps. A mirror
test pins both halves and that direction, and a second test runs the real script
against a scratch ledger deferring in both arrays, since a test of the helper alone
stays green when either call site is deleted.

The ledger's one `investigate` row now tracks #1553, which holds the outstanding
`pyproject.toml` question, rather than #669. Pointing it at #669 while closing
#669 as fixed would leave the only governed row naming a closed issue where
nothing is being decided, which is the state #669 was filed about, with both new
gates reading clean over it.

Refs #669

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 12, 2026 01:36
@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 9650c11d-44f9-459f-9397-9d5a8b563038

📥 Commits

Reviewing files that changed from the base of the PR and between 4486984 and 4c31860.

📒 Files selected for processing (5)
  • README.md
  • scripts/tests/test_spec_validate.py
  • spec/divergences.json
  • spec/divergences.schema.json
  • spec/validate.py

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The divergence ledger schema and validator now require non-empty tracking values for investigate entries. The requirement applies to both dispositions and gaps, with tests covering helper behavior, wiring, and schema alignment.

Changes

Divergence tracking enforcement

Layer / File(s) Summary
Tracking contract and ledger updates
spec/divergences.schema.json, spec/divergences.json, README.md
The schema conditionally requires non-empty tracking for investigate entries in both arrays. The ledger description and tracking reference are updated.
Validator enforcement
spec/validate.py
The validator checks investigate entries in both dispositions and gaps and rejects missing or whitespace-only tracking values.
Contract and wiring tests
scripts/tests/test_spec_validate.py
Tests cover helper behavior, both validation loops, schema conditionals, and the stricter whitespace check.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 4c318

The tracking requirement is consistently documented and enforced for investigate entries, with coverage for both dispositions and gaps.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.78% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 2 files. (3 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: requiring a tracking value for investigate dispositions.
Linked Issues check ✅ Passed The PR addresses the coding requirement in #669. spec/divergences.schema.json conditionally requires a non-empty string tracking value for investigate entries in both dispositions and gaps. …
Out of Scope Changes check ✅ Passed The changes stay within #669 scope. The README and ledger note document the new requirement. The ledger updates the pending pyproject.toml decision to successor issue #1553. The schema, validator, a…
Full details: Docstring Coverage

Explanation

Docstring coverage is 27.78% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 2 files. (3 skipped: 3 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/669-require-tracking-on-investigate

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ptr727

ptr727 commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The schema, validator, tests, and documentation changes are consistent and collectively enforce the new investigate tracking invariant without impacting other dispositions.

Pull request overview

This pull request makes tracking structurally required for divergence-ledger entries whose disposition is investigate, ensuring a deferred decision always names where that decision is being made (schema + validator enforcement), and updates the ledger/docs to reflect the new invariant.

Changes:

  • Add an investigate_tracking_errors validator helper and wire it into both the dispositions and gaps loops in spec/validate.py.
  • Extend spec/divergences.schema.json with a shared $defs conditional (investigateNeedsTracking) to require a non-empty tracking string when disposition == "investigate".
  • Update the live ledger entry and README prose to document/reflect the investigate-only requirement.
File summaries
File Description
spec/validate.py Adds and wires a validator helper enforcing non-empty tracking for investigate dispositions.
spec/divergences.schema.json Adds a $defs conditional and applies it to both ledger arrays to require tracking for investigate.
spec/divergences.json Updates the remaining investigate row’s tracking to the successor issue and documents the new asymmetry in the note.
scripts/tests/test_spec_validate.py Adds unit + integration-style tests proving the helper behavior and its wiring into both loops, plus schema mirror assertions.
README.md Updates the divergence-ledger description to document the investigate-only tracking requirement.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@ptr727

ptr727 commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

Declining the Docstring Coverage pre-merge warning (27.78 percent against a required 80 percent), with evidence rather than an assertion, and filing the class question rather than re-arguing it per pull request.

Why it does not apply here:

  • No gate in this repository enforces docstring coverage. No CI step, composite action, or hook runs a docstring tool, and pyproject.toml's ruff configuration sets extend-select = ["I"] with the D family unselected.
  • The 80 percent is CodeRabbit's default, not a fleet decision. .coderabbit.yaml configures no docstring rule, while it does deliberately settle four other tools.
  • The threshold does not describe this code. The touched file's methods sit at 49.6 percent measured before this branch, 63 with a docstring against 64 without.
  • Precedent. Pass the Branch to Every run: Script Through env, Never as an Expression #1547 carried this identical warning and merged.

What this branch actually added, so the number is not left to speak for itself: all three new test classes carry a class docstring, the new investigate_tracking_errors helper carries one, and 3 of 13 new methods carry one. Those three are exactly the methods whose docstring states something the method name does not, why a blank string matters, why the scoping case exists, and which direction of schema-to-gate disagreement is the safe one. The other ten are named declaratively, and a docstring restating the name is the comment growth CODESTYLE.md discourages.

Stated plainly rather than buried: at 23.1 percent the methods added here do sit below that 49.6 percent baseline, so this is a deliberate style judgment about self-describing test names and not a claim that the additions match the file's rate.

The class question, whether this check should be disabled the way markdownlint, actionlint, shellcheck and ruff already are, or given a threshold that matches the fleet's real convention, is filed as #1558.

@ptr727
ptr727 merged commit 02646f7 into develop Sep 12, 2026
9 checks passed
@ptr727
ptr727 deleted the feature/669-require-tracking-on-investigate branch September 12, 2026 01:48
ptr727 added a commit that referenced this pull request Sep 12, 2026
)

Promotes `develop` to `main`, carrying one change.

## Require a Tracking Value on an investigate Divergence Disposition
(#1556)

`investigate` is the one disposition in the divergence ledger's
vocabulary that names no outcome. The other five each state what happens
to the divergence, where this one states only that nobody has decided
yet, so an entry carrying it beside a null `tracking` recorded that a
decision was owed while naming nowhere it was being made, and the
deferral read as an omission. That requirement is now structural: a
`$defs` conditional in `spec/divergences.schema.json` applied to both
arrays, the same check in `spec/validate.py` so CI refuses such an entry
rather than an editor alone, and the asymmetry recorded in the ledger's
own note and in `README.md` beside the entry shape.

The field stays optional under every other disposition, each of which
already names its own outcome, and a test holds that scoping. The schema
and the gate are deliberately unequal in one direction only, the gate
additionally refusing a whitespace-only value, because a schema pattern
closing that gap would refuse a value the gate accepts, which is the
direction #1504 reverted for the registry schema.

## What this does not settle

The ledger's one `investigate` row tracks #1553 rather than #669, which
is why closing #669 here is honest: the rule it asked for has shipped,
and the decision that row defers has its own issue to be decided in.
Three follow-ups stay open deliberately, none of them blocking this
promotion:

- #1553, whether the Python repos carry an equivalent config-only
`pyproject.toml`. Its measurement half is answered on the issue: all
three carriers hold one, none holds an equivalent, and tool placement is
not a fleet constant. What remains is the fleet-wide new-findings
tradeoff, which is the maintainer's call.
- #1554, `reports/divergences.md` lagging the ledger's tracking values
in two rows. It is generated rather than hand-edited and regenerating is
a networked run, so it lands on its own where the fleet drift it reports
is what is being read.
- #1555, `TODO.md`'s Stage 4 note calling `validate-task.yml` an
`investigate` gaps entry where the ledger dispositions it `retire`.
Pre-existing, and it predates this change's merge base.

## Verification

Four local adversarial passes ran over this change, the last finding
nothing in any category. Between them they corrected a false citation
twice, moved the governed ledger row off the issue being closed, and
added a wiring test after a pass showed that deleting either `main()`
call site kept every test green. Five mutants are killed by the current
tests, including one at each call site. A real `jsonschema` probe over
192 entry shapes confirms the schema is stricter than the gate nowhere.
On #1556: every CI job passed and Copilot reviewed the full head with no
findings.

Closes #669

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants