Skip to content

lean-jev: require the docs reads, refuse an unnamed blob of state - #26

Merged
mishioo merged 1 commit into
mainfrom
lean-jev-require-docs
Oct 5, 2026
Merged

mishioo merged 1 commit into
mainfrom
lean-jev-require-docs

Conversation

@mishioo

@mishioo mishioo commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

A session used ask-jev to find duplicate and contradicting entries in a lessons log, sent about 400 questions over five runs, and produced findings that did not hold. It reported itself afterwards, which is how this got found.

What happened

The agent read SKILL.md and nothing else. It never fetched primitives/choice.md, primitives/noul.md or concepts/state.md, although step 2 of the loop told it to. It sent the whole log as one plain string ("L1. ... L2. ...") and referred to entries in prose as "lesson L3", with choice options labelled Lesson L3. It then drew conclusions about which question shapes work from a state that was badly framed to begin with, and presented them as findings.

What let it happen

  1. The fallback clause. "When the fetch fails, offline, sandboxed, or blocked, the contract above is enough to make a well-formed call" read as an opt-out. The fetch never failed; it was never attempted. The web tool was deferred in that session, so reading cost an extra step, and the contract looked complete on its own.
  2. The disclosure rule attached to it asked the agent that skipped the reads to state that it had. It fails in exactly the case it exists for, and it makes skipped work look disclosed.
  3. The only inline example passed state as a bare string, so the shape a reader copies was the wrong default for anything with more than one part. The same example sat in README.md.
  4. "Read that type's page" sat inside step 2 of the loop, below a request contract that looked self-sufficient. Nothing came before the first call.

What changes

The docs section moves above the contract as Before the first call: concepts/state.md every time, plus the page for each question type in use. It says outright that the contract is not a substitute, because a request can satisfy it, ignore the pages, and return a confident number about the wrong thing with nothing in the response to mark it. A page that will not load is now a reason to stop and tell the user, and the disclosure clause goes with the fallback it served.

Both examples now pass an object and point a question at one of its fields, matching the shape evaluating-a-test.md already uses.

jev.py refuses a state over 400 characters passed as unnamed text, names concepts/state.md, and offers --state-format text for a genuine single blob such as a diff or a log. This is the only part that is actually enforced. Prose cannot make an agent read a page, so the check goes where the missed read does its damage: it fires before the network call and exits 1, so the guidance arrives at the one moment an agent cannot skip.

It catches the blob, not the skipped read. An agent can still write poor criteria over a well shaped state. Worth stating rather than treating the skill as now safe.

Verification

The documented example runs verbatim: jev-1.13.0, is_bug 0.97, 300 input tokens. The refusal path exits 1 before spending a call, with the guidance in the message. 25 tests pass, four of them new: long blob refused, long blob allowed when declared text, long object untouched, blob from a file refused too. claude plugin validate . --strict passes.

A README.md troubleshooting row covers the new refusal. Version 0.3.0.

Note for the internal marketplace

I opened the same change as leancodepl/ai-plugins-internal#126 before noticing the plugin had already moved here, and have closed it. plugins/lean-jev still needs adding to includePaths in the internal sync-policy.json for this to flow inward.

🤖 Generated with Claude Code

A session sent about 400 questions with its whole input as one plain string,
having read no TypeSafe docs page, and drew conclusions from the answers. The
skill allowed it. A fallback clause said the request contract was enough when a
fetch failed, the fetch was never attempted, and the rule meant to catch that
asked the agent which skipped the reads to disclose that it had.

The docs section moves above the contract as a precondition, naming
concepts/state.md every time plus the page for each question type in use, and
states that the contract is not a substitute for it. A page that will not load
is now a reason to stop and tell the user.

jev.py refuses a state over 400 characters passed as unnamed text, names
concepts/state.md, and offers --state-format text for a genuine single blob.
Prose cannot make an agent read a page, so the one mechanical check goes where
the missed read does its damage, before the call is spent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mishioo
mishioo marked this pull request as ready for review September 25, 2026 08:57
@mishioo mishioo self-assigned this Sep 25, 2026
@mishioo
mishioo requested a review from wzslr321 September 25, 2026 09:37

@wzslr321 wzslr321 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

sorry for late review, somehow I missed this PR :(

@mishioo
mishioo merged commit 61b50bd into main Oct 5, 2026
4 checks passed
@mishioo
mishioo deleted the lean-jev-require-docs branch October 5, 2026 08:04
@mishioo

mishioo commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

sorry for late review, somehow I missed this PR :(

No worries

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