Repository navigation
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (1)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review. 📝 SummarySummary by CodeRabbit
WalkthroughSchema shaping now checks property-local definition blocks before root blocks. Pointer validation follows each distinct local reference once with an iterative walk. Tests cover definition precedence, invalid local blocks, long and nested reference chains, and wide schemas. ChangesSchema reference resolution
Priority: ⬇️ Low Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix Merge Risk: ⚪ Minimal · up to No identified issue blocks merging; run the normal checks before merging. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
|
👋 The PR description doesn't fully follow
Please update the PR description so reviewers have the context they need. |
hello-alexmcc
left a comment
There was a problem hiding this comment.
Rule-17 review of #2886 at 55f49d2: comment. I'll approve once the three open threads are answered; the first needs a code change.
- The cap counts breadth as well as depth (schema.rs:156): agreed, and it will bite real schemas.
followedis never popped, so 64 bounds the distinct pointers a property reaches in all. A Pydantic- or OpenAPI-derived tool schema, whose properties often name dozens of$defs, crosses it with no chain at all, and the property then loses its type pin. What the stack needs bounded is depth. A path stack does it: push on the way into a definition, pop on the way out; refuse past N levels, and treat a pointer already on the path as a cycle. A memo of the definitions already found to resolve keeps a shared definition from being walked again, so wide schemas stay linear. - A non-object
$defsin the property (schema.rs:125): agreed. The check andattached_toshould treat"$defs": nullor[]the same way. Either both skip it for the root's entries, or both treat the pointer as unresolved, so the check never passes a document the engine can't compile. - The chain test (xtml.rs:480): agreed. With
ptyped itself, the short chain pinsobjectand the long one pins nothing, so the test follows the walk's result rather than the one-hop rule. A wide-schema case (65 distinct leaf definitions, typed) would pin item 1.
What holds at this head (on main 3c430bd):
- Gates: fmt, clippy and doc exit 0; tests 332 lib, 10 fixture, 7 contract and 5 README. The 5,000-link chain no longer overflows.
- Dump: every table's grammar is identical to main's (180 grammars).
- Parity: GLM and DSML 36 of 36. Kimi K3 vs the old builder differs only by
"type": "tag", plus the{"$ref": "#"}property already noted on #2881. Definitions-first resolution changes nothing my 14 tool sets reach. - Trailers: six in the squash body, and no AI trailer.
hello-alexmcc
left a comment
There was a problem hiding this comment.
Rule-17 re-review of #2886 at 396be3e: comment. One more round; the new thread is right.
The three earlier threads are settled. The wide schema stays pinned, the chain test follows the walk, and a non-object own block resolves nothing. Gates exit 0 (334 lib), every table's grammar is identical to main's, and the Kimi K3 payloads are identical to 55f49d2's.
The new thread (schema.rs:176): agreed. depth counts pointer hops, but the walk also recurses through each definition's own JSON nesting between hops. serde_json caps a document at 128 levels, but that bounds where a definition sits, not the chain through definitions. So 64 hops of ~120 levels each give ~7,700 nested calls from a request's tool schema, past the 5,000-link chain the test treats as safe.
Either of two shapes closes it:
- One counter for every descent (into a value, an array item, or a pointer's target), capped, with anything past the cap treated as unresolved, so the property takes any value.
- An iterative walk with an explicit work stack, so nothing request-shaped sits on the call stack at all.
A test with the thread's shape (64 definitions, each nesting ~120 levels before its $ref) would pin it.
…own definitions first A chain of distinct definitions sent the walk one level deeper per link, with nothing but cycle detection to stop it, and a request could chain enough of them to exhaust the stack. The walk follows at most sixty-four pointers in all; past that a pointer counts as unresolved and the property pins nothing. And a property's own `$defs` or `definitions` stand before the root's when a pointer is resolved, as they do in the document the engine gets, so a property that defines a name the root defines badly keeps its type. Signed-off-by: Simo Lin <25425177+slin1237@users.noreply.github.com> Co-authored-by: Chang Su <8605658+CatherineSue@users.noreply.github.com> Co-authored-by: Keyang Ru <rukeyang@gmail.com> Co-authored-by: key4ng <key4ng@users.noreply.github.com> Co-authored-by: Chen Hongtao <56470055+chenht2022@users.noreply.github.com> Co-authored-by: LightSeek Foundation <243258330+lightseek-bot@users.noreply.github.com>
…object block resolves nothing The cap on pointers followed counted every distinct pointer, so a wide schema, a Pydantic model whose tree names more than sixty-four definitions with no chain at all, lost its type pins. The memo of pointers followed stays, so each is walked once and a wide schema costs one walk per definition, and a depth counter bounds the chain instead. A block the property has but that is no object (`"$defs": null`) now resolves nothing, as the document the engine gets has no entry at the pointer. The chain test types its property, so its outcome depends on the walk, and pins the boundary; a wide schema and the non-object block have tests of their own. Signed-off-by: Simo Lin <25425177+slin1237@users.noreply.github.com> Co-authored-by: Chang Su <8605658+CatherineSue@users.noreply.github.com> Co-authored-by: Keyang Ru <rukeyang@gmail.com> Co-authored-by: key4ng <key4ng@users.noreply.github.com> Co-authored-by: Chen Hongtao <56470055+chenht2022@users.noreply.github.com> Co-authored-by: LightSeek Foundation <243258330+lightseek-bot@users.noreply.github.com>
… at, and follows a chain to its end The depth cap counted pointer hops, and between hops the walk still recursed through each definition's own nesting, so a chain of sixty-four definitions each nested a hundred levels deep put some seven thousand frames on the stack, more than the five-thousand-link chain the test called the unsafe case. The walk now keeps the schemas still to look at in a list of its own and follows a chain to its end: each distinct pointer is followed once, as before, and nothing a request sends reaches the call stack, whatever its chains and nesting. The depth constant goes. The chain test pins ten links, five thousand links, and sixty-four links each wrapped a hundred and twenty times in `properties` before their pointer, all followed to the integer at the end. Signed-off-by: Simo Lin <25425177+slin1237@users.noreply.github.com> Co-authored-by: Chang Su <8605658+CatherineSue@users.noreply.github.com> Co-authored-by: Keyang Ru <rukeyang@gmail.com> Co-authored-by: key4ng <key4ng@users.noreply.github.com> Co-authored-by: Chen Hongtao <56470055+chenht2022@users.noreply.github.com> Co-authored-by: LightSeek Foundation <243258330+lightseek-bot@users.noreply.github.com>
396be3e to
c9ed9cd
Compare
…ast link Every chain case pinned `object`, which a walk that stopped after the first hop, or dropped its pending schemas past some depth, would also have given, since every link resolves and the last definition is a whole `integer`. The same chains with their last definition missing now have to come out typeless: only a walk that reaches the end finds the pointer at nothing, so the pin shows the property the test's name promises. Signed-off-by: Simo Lin <25425177+slin1237@users.noreply.github.com> Co-authored-by: Chang Su <8605658+CatherineSue@users.noreply.github.com> Co-authored-by: Keyang Ru <rukeyang@gmail.com> Co-authored-by: key4ng <key4ng@users.noreply.github.com> Co-authored-by: Chen Hongtao <56470055+chenht2022@users.noreply.github.com> Co-authored-by: LightSeek Foundation <243258330+lightseek-bot@users.noreply.github.com>
hello-alexmcc
left a comment
There was a problem hiding this comment.
Rule-17 re-review of #2886 at b475a59: approve. This round only touches the test, and all five threads are answered and resolved.
- The chain test now proves the walk reaches the end:
chain(..., whole: false)leaves the last definition out. The 5,000-link chain and the 64 × 120 chain then come out typeless, beside the three whole chains that pinobject. A walk that stopped early would fail it. - From c9ed9cd: the iterative walk, the wide-schema pin and the non-object own block, verified there. No table's grammar changes, and the Kimi K3 payloads are identical to the earlier rounds'.
- Gates at b475a59: fmt, clippy and doc exit 0; tests 344 lib, 10 fixture, 7 contract and 5 README.
- Trailers: six in the squash body, and no AI trailer. The merge is the maintainer's.
What
Two fixes to
grammar/schema.rsthat the review of #2881 asked for after that PR had been merged (its last two threads, answered at a commit the merge did not include), insidecrates/symphony/only:pointers_resolvefollows every pointer it resolves into the definition it names; cycle detection alone let a request's chain of distinct definitions (D0 → D1 → … → Dn) send a recursive walk one frame deeper per link, and each link's own nesting rode on the stack too, so a long or deep enough chain would exhaust the stack and abort the process. The walk now pops the schemas still to look at from a list of its own, with the memo of pointers followed kept (each distinct pointer is followed once, so a wide schema costs one look per definition), and nothing a request sends reaches the call stack. The chain test types its property so the outcome depends on the walk, and pins ten links, five thousand links, and sixty-four links each wrapped a hundred and twenty times inpropertiesbefore their pointer, all followed to theintegerat the end, while the same five-thousand-link and sixty-four-deep chains with their last definition missing come out typeless (only a walk that reaches the end finds the pointer at nothing); a wide-schema test with seventy leaf definitions stays pinned."$defs": null,"$defs": [], an SDK writing an empty map as null) resolves nothing, asattached_toleaves it in place with no entry at the pointer; before, the pointer fell through to the root and the engine got a schema it could not compile.attached_to) keeps the property's own$defsanddefinitionsand fills in the root's, so a pointer is now resolved the same way (Definitions::within): a property that defines a name the root defines badly keeps its type, and a sibling that reaches the root's dangling definition pins nothing.a_propertys_own_definition_stands_before_the_rootspins both.No grammar on main changes for any table in the test corpus; both cases are new shapes.
Gates
By exit code (
.claude/symphony/gates.sh): fmt, clippy-D warnings, test (344 lib, 10 fixture, 7 contract, 5 README), doc, width, codespell, all 0.Merge
Squash under rule 25 (rule 18 does not apply: nothing of
Format's changes). Subject: the PR title plus(#<n>). Body, with the authors of the old Kimi K3 parser whose definitions walk this code descends from (rule 27, as on #2881):