B-0024: measure the command↔prose join, then grade property lookups with it - #134
Conversation
… not a key Answers B-0024's gating question — is fragment-grained menu-path extraction precise and fine enough to be the missing command→property key? Measured on the CI artifact v0.11.2-alpha.109 (schema 11, source_commit 4cd7413): - 76.1% conditional precision, 6.5x narrower candidate set than page grain - but 42.7% of property-owning sections name no menu path, and 63.6% of property rows share a section with 10+ others So it is adopted as a ranking signal and rejected as a key. It discriminates correctly on both motivating families, including their negative cases: #131's pvid (exact on the two bridge-port sections, no signal on the Apps row) and #58's address-pool (exact on DHCP, no signal on the HotSpot mislink targets). Two sub-questions close outright. Table granularity is dead: no table names a path its section lacks and 495 of 562 name none, so a table-grained key can only lose information. Proximity — the one remaining candidate for a real key — needs new extraction-time provenance; no line/offset column exists on sections, page_tables, page_table_rows, or properties, correcting a claim in the briefing that they retain line spans. The measurement also surfaced two defects in the linker shipping today: bare top-level menus (/certificate, /queue) are invisible to MENU_PATH_RE, and normalizeMenuPath fabricates pseudo-paths from assignments. Both are recorded with their measured cost; neither is fixed here, since the first changes commands.page_id and needs a link diff rather than unit tests. Extraction moves to src/menu-paths.ts (pure, DB-free, anchor-tested) so the census and the linker measure the same rules instead of drifting copies. The link-commands.ts change is a behaviour-preserving refactor. Refs #131, #58, #61. Briefing records the step-4 confidence contract the bounds support. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… step 4)
`routeros_lookup_property`'s confidence described which SQL ran: `high` meant
"the scoped branch found rows on the page `commands.page_id` links to" and
`low` meant "it didn't". So every property on a linked page was equally `high`,
and a correct row reached by the global fallback was always `low`.
Step 3 measured the alternative — menu paths named in the property's own
section — at 76.1% conditional precision but silent for 42.7% of sections: a
ranking signal, not a key. This uses it to grade rather than to select. The row
set is unchanged; only the label and the order move.
`src/property-confidence.ts` scores each row against the requested menu:
high the row's own section names that menu, that menu is the one the
section is about, and the command tree does not contradict it
medium the section names a neighbouring menu, or names this one only as a
cross-reference, or only the page aligns
low nothing but the property name ties the row to the menu
Over the 14,832 (menu, name) pairs the command tree says are real: 31.5% of
today's `high` labels survive, 2,295 demote, and 1,940 rows escape a wrongly
`low` one. `/interface/bridge/port` + `pvid` now ranks the two bridge-port
sections above the unrelated Apps row (#131); `/ip/dhcp-server` +
`address-pool` ranks the DHCP server table above the DHCPv6 one (#58).
The support gate answers step 4's open question, and the retrieval eval is why
it exists: without it, `/ip/firewall/filter` + `action` scored `high` on the
*bridge* firewall section, which cites that menu in one sentence while
documenting `/interface/bridge/filter`. Requiring the requested menu to accept
the most of the section's own property names keeps 84.6% of `high` labels; the
blunter "reject any section naming two menus" keeps 41.9% and discards correct
alignments over incidental cross-references.
The eval's property assertion moves with the contract rather than against it:
`high` alone is no longer the bar, since a correct row whose section names no
menu is honestly `medium` (WireGuard's bare property table). It now requires
the top-ranked row to be the expected page and not `low`, and still requires
`high` where no page is expected — which keeps the known-failing BL-1 anchor
red, as it should be.
Refs #131, #58, #61
📝 WalkthroughWalkthroughShared RouterOS menu-path extraction utilities normalize, validate, and resolve paths. Property rows receive ChangesProperty confidence and menu-path alignment
Estimated code review effort: 4 (Complex) | ~50 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant lookupProperty
participant gradeRows
participant Database
participant acceptanceMap
Client->>lookupProperty: request property with command_path
lookupProperty->>Database: fetch all matching rows with section_id
lookupProperty->>gradeRows: grade scoped rows against command_path
gradeRows->>Database: read section paths and command acceptance
gradeRows->>acceptanceMap: resolve property name to accepted menus
gradeRows-->>lookupProperty: return confidence tiers
lookupProperty-->>Client: return sorted rows by tier
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Pull request overview
Implements B-0024 steps 3–4 by (1) adding a corpus-wide census to measure whether section-level menu-path mentions can key the command↔prose join, and (2) using that result to recalibrate routeros_lookup_property confidence so it grades each returned row against the requested menu (and orders best evidence first), rather than reflecting which SQL branch ran.
Changes:
- Add
menu-paths.tsas the shared, DB-free menu-path extractor and refactorlink-commands.tsto use it. - Introduce
property-confidence.tsand wire it intolookupProperty()so confidence tiers are per-row and results are tier-ranked. - Add/adjust evaluation + validation artifacts (new census script, retrieval eval fixture changes, tests,
VALIDATION.md, andCHANGELOG.mdentry).
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| VALIDATION.md | Adds a blocking validation row for the new property-confidence contract and its tests. |
| src/query.ts | Wires per-row property confidence grading + tier-based ordering into lookupProperty(). |
| src/query.test.ts | Extends DB fixture and assertions to anchor the new confidence/ordering rules. |
| src/property-confidence.ts | Implements evidence gathering + tier rules (high/medium/low) for property rows. |
| src/property-confidence.test.ts | Pins the tier decision table and support-gate behavior as pure-function tests. |
| src/menu-paths.ts | Factors menu-path extraction into a shared module for linker + census + grading. |
| src/menu-paths.test.ts | Anchors extraction/normalization/resolve behavior with focused unit tests. |
| src/mcp.ts | Updates routeros_lookup_property tool description to describe new ordering/tier meaning. |
| src/link-commands.ts | Switches linker path extraction to use extractMenuPaths() from the new module. |
| src/eval/retrieval.ts | Updates property-surface eval assertions to match the stricter per-row tier semantics. |
| src/eval/command-prose-join.ts | Adds the step-3 join census script that prints provenance + measured outcomes. |
| project-words.txt | Adds new corpus/eval terminology to spellcheck allowlist. |
| fixtures/eval/queries.json | Updates notes to reflect new tier semantics (e.g., WireGuard now “medium” not “high”). |
| CHANGELOG.md | Adds an Unreleased “Changed” entry documenting user-visible confidence semantics change. |
| briefings/B-0024-command-prose-join.md | Promotes Option A from hypothesis to measured result and records conclusions + follow-ups. |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@briefings/B-0024-command-prose-join.md`:
- Around line 524-526: Update the open-question entry for “What is high allowed
to mean?” in the Step 4 discussion to mark it resolved, removing the statement
that the support-ratio gate remains undecided. Align it with the established
requirement that the support-ratio filter must gate high and the shipped gate
noted later in the document.
- Around line 317-319: Correct the table-granularity claim in the prose to match
the census categories emitted by the command-prose-join analysis, treating the
count of 33 as the narrower category rather than stating that tables are never
wider than their sections. Also revise the repeated absolute claim “No table
names a path its section lacks” to use wording consistent with the actual
wider/disjoint counts, while preserving the surrounding Option A reasoning.
In `@src/eval/command-prose-join.ts`:
- Around line 63-77: Extract the shared arg-row-to-accepted-menus builder
currently duplicated in the local acceptsByName construction and
property-confidence.ts into the existing shared menu-paths utility or another
shared module, then consume it from both callers. Preserve the parent-menu
derivation via lastIndexOf("/"), and explicitly verify whether keys should
remain raw names here or consistently use name.toLowerCase() as acceptanceMap
does before choosing the shared contract.
In `@src/mcp.ts`:
- Around line 790-800: Update the confidence definitions in the command_path
guidance near routeros_command_tree so high confidence requires only
non-contradictory command evidence, not always an affirmative command-tree
match. Classify rows supported by documentation cross-reference alone as medium,
while preserving the existing parent/child and page-match medium cases and the
low tier for property-name-only candidates.
In `@src/property-confidence.ts`:
- Around line 98-114: Update supportedPaths to compute support even when paths
contains a single menu, preserving the existing best === 0 fallback for truly
unsupported sets. Ensure the caller’s confidence logic demotes the result to
medium when best is zero and the command tree recognizes at least one section
property name, rather than allowing that row to remain high solely because one
path was provided.
- Around line 139-152: Update gradeRows to lazily cache the directory paths in a
module-scoped cachedDirPaths/dirPathSet helper, rather than querying commands on
every lookup. Also hoist and reuse the sectionQuery, namesQuery, and
acceptanceQuery prepared statements at module scope so each gradeRows call only
binds and executes the existing queries.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 171c5718-99a2-46c2-9b2a-0678992af3eb
⛔ Files ignored due to path filters (1)
fixtures/eval/queries.jsonis excluded by!fixtures/**
📒 Files selected for processing (14)
CHANGELOG.mdVALIDATION.mdbriefings/B-0024-command-prose-join.mdproject-words.txtsrc/eval/command-prose-join.tssrc/eval/retrieval.tssrc/link-commands.tssrc/mcp.tssrc/menu-paths.test.tssrc/menu-paths.tssrc/property-confidence.test.tssrc/property-confidence.tssrc/query.test.tssrc/query.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
- GitHub Check: copilot-pull-request-reviewer
- GitHub Check: Analyze (actions)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use Bun and TypeScript, including Bun-native runtime APIs such as
bun:sqlite,Bun.serve, andbunx.Rosetta uses Bun and TypeScript; prefer
bun,bun test, andmake verifywhere applicable, rather than Node/npm-oriented substitutes.
Files:
src/eval/command-prose-join.tssrc/eval/retrieval.tssrc/mcp.tssrc/menu-paths.test.tssrc/property-confidence.test.tssrc/menu-paths.tssrc/link-commands.tssrc/query.test.tssrc/query.tssrc/property-confidence.ts
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Rosetta is read-only documentation/schema context and must not connect to or modify a user's router.
Files:
src/eval/command-prose-join.tssrc/eval/retrieval.tssrc/mcp.tssrc/menu-paths.test.tssrc/property-confidence.test.tssrc/menu-paths.tssrc/link-commands.tssrc/query.test.tssrc/query.tssrc/property-confidence.ts
**/*.{ts,tsx,md}
📄 CodeRabbit inference engine (AGENTS.md)
Keep the attribution boundary visible when community RouterOS skills surface, because they are supplemental rather than official MikroTik documentation.
Files:
src/eval/command-prose-join.tssrc/eval/retrieval.tsCHANGELOG.mdsrc/mcp.tssrc/menu-paths.test.tssrc/property-confidence.test.tsVALIDATION.mdsrc/menu-paths.tssrc/link-commands.tssrc/query.test.tssrc/query.tssrc/property-confidence.tsbriefings/B-0024-command-prose-join.md
*
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Keep this Copilot instructions file short; put substantive rules in narrow instruction files under
.github/instructions/*.instructions.md.
Files:
CHANGELOG.mdproject-words.txtVALIDATION.md
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.md: Keep each project documentation file limited to its canonical role; prefer the documented canonical home instead of creating a new top-level Markdown file.
Do not duplicate content from this routing index; move operational detail, schema blocks, or long rule lists to their canonical documentation or instruction files.
Apply the repository's Markdown linting and fenced-code conventions, while excluding LLM instruction files where the repository explicitly requires that exclusion.
Files:
CHANGELOG.mdVALIDATION.mdbriefings/B-0024-command-prose-join.md
CHANGELOG.md
📄 CodeRabbit inference engine (CLAUDE.md)
Record user-visible shipped changes in
CHANGELOG.mdunder[Unreleased]or release sections.
Files:
CHANGELOG.md
src/{query,mcp,browse}.ts
📄 CodeRabbit inference engine (AGENTS.md)
Route MCP, query, classifier, TUI, and canonicalizer behavior through shared core code, usually
src/query.ts; keepsrc/mcp.tsandsrc/browse.tsas thin adapters.
Files:
src/mcp.tssrc/query.ts
VALIDATION.md
📄 CodeRabbit inference engine (CLAUDE.md)
Document load-bearing invariants and how CI proves them in
VALIDATION.md.
Files:
VALIDATION.md
briefings/B-*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Store grounded research and decision support in
briefings/B-*.md.
Files:
briefings/B-0024-command-prose-join.md
🪛 LanguageTool
VALIDATION.md
[uncategorized] ~25-~25: The official name of this software platform is spelled with a capital “H”.
Context: ...Property confidence) via bun testin.github/workflows/test.yml| blocking |#131` |...
(GITHUB)
briefings/B-0024-command-prose-join.md
[style] ~208-~208: ‘none at all’ might be wordy. Consider a shorter alternative.
Context: ... sort_order, and properties carries none at all. Verified against v0.11.2-alpha.109; ...
(EN_WORDINESS_PREMIUM_NONE_AT_ALL)
[style] ~290-~290: Consider an alternative for the overused word “exactly”.
Context: ...xcellent or entirely spurious, which is exactly the shape a support-ratio filter can ...
(EXACTLY_PRECISELY)
[style] ~533-~533: ‘out of reach’ might be wordy. Consider a shorter alternative.
Context: ... - #61's prose-only properties remain out of reach. Section alignment needs a section; c...
(EN_WORDINESS_PREMIUM_OUT_OF_REACH)
🔇 Additional comments (18)
src/menu-paths.ts (2)
22-52: LGTM!
59-97: LGTM!src/link-commands.ts (2)
22-26: LGTM!Also applies to: 81-82
70-72: 🗄️ Data Integrity & IntegrationNo change needed.
cmdPathToIdis only populated beforedirPathSet, and the remaining references are reads.src/menu-paths.test.ts (1)
1-79: LGTM!src/eval/command-prose-join.ts (1)
30-57: LGTM!Also applies to: 79-338
src/property-confidence.ts (2)
68-89: LGTM!
154-202: LGTM!Also applies to: 204-228
src/property-confidence.test.ts (1)
10-122: LGTM!briefings/B-0024-command-prose-join.md (1)
157-157: LGTM!Also applies to: 189-194, 206-211, 243-316, 320-337, 436-481
src/query.ts (3)
12-16: LGTM!Also applies to: 1059-1059
1085-1145: LGTM!
1083-1083: 🎯 Functional CorrectnessNo duplicate type alias remains.
PropertyLookupRowUngradedappears only once, so there is no duplicate identifier issue to address.> Likely an incorrect or invalid review comment.src/query.test.ts (1)
211-296: LGTM!Also applies to: 1077-1080, 1092-1137, 1156-1159
src/eval/retrieval.ts (1)
344-358: LGTM!CHANGELOG.md (1)
39-39: LGTM!VALIDATION.md (1)
25-25: LGTM!project-words.txt (1)
11-20: LGTM!Also applies to: 40-40, 143-143, 235-235, 264-264, 372-373, 447-447
There was a problem hiding this comment.
Codex Review
Independent review found two additional issues in the inline comments below.
One process loose end: this PR explicitly leaves the source linkage/property gaps open, so .github/instructions/issue-pr-linking.instructions.md calls for Part of #131, Part of #58, and Part of #61 in the PR body rather than Refs .... Please update that relationship before merge so the partial landing is represented consistently.
…gate The tier system could still be vetoed by the link it was built to survive. While the scoped branch selected the rows, a mislinked page that happened to document the same property name kept the right row out of the result entirely: `/interface/bridge/host` + `vid` returned two `medium` rows from the IGMP snooping page and never graded the `high` "Static Entries" section that names `/interface/bridge/host` outright. Same for `/interface/wifi/provisioning` + `radio-mac`. Caught in review, verified against the alpha.109 artifact. Candidates now come from every page documenting the property, with page alignment applied per row. Off-page rows must earn their place: once the linked page contributes anything, unaligned (`low`) rows elsewhere are dropped, so a correct link returns the tight result it always did. The support gate no longer exempts single-path sections, which was precisely backwards — a section naming one menu has no competitor to be measured against, so a lone passing mention was the least contradicted and most likely to be mistaken for authority. Scoring now separates the two zeroes: the command tree knowing none of the section's names is silence and cannot demote; knowing them and finding no named menu that takes any is evidence against every candidate. The step-4 figures are now produced by committed code rather than a scratch script. `eval/command-prose-join.ts` replays lookupProperty's candidate set and filter and grades with the shipped gradeRow/supportedPaths, so the briefing and CHANGELOG numbers regenerate after any rule change. Restated on that basis: 33.4% of `high` labels survive, 1,940 rows escape a wrong `low`, and 236 rows that were suppressed outright are now returned. Also from review: - `acceptanceMap` moves to property-confidence.ts and the census consumes it, so the census cannot describe rules the grader does not follow. Keys are lowercased in both; the census previously keyed raw names. - `dir` set and prepared statements cached lazily instead of per lookup. - MCP tool description covers the support gate and the cross-reference case. - TOP_LEVEL_MENUS doc no longer implies resolved extraction keeps roots with no `dir` row behind them. - Briefing: table-granularity counts stated per census category (identical 33, narrower 34, wider 0, disjoint 0); the `high` open question closed. Refs #131, #58, #61
…igures Widening the candidate set introduced a regression the reviewer caught: tiers are menu-level, so a ubiquitous name is legitimately `high` on several pages at once. `name` at /interface/ethernet grades `high` on Ethernet, Bonding and PoE-Out, ties were broken by page title, and explainCommand reported `/interface/ethernet set name=ether2` as "Name of the bonding interface" — a worse answer than before this branch, on a query the old scoped branch got right. Linked-page rows now win ties within a tier. The page link is weak evidence but it is still evidence, and it is the right thing to break a tie the section evidence cannot. Retention tightens with it: an off-page row is kept only if its tier is at least as good as the best the linked page offers, which subsumes the previous drop-`low` rule and collapses that lookup from 16 rows to 4. The suppressed-row fixes are unaffected — /interface/bridge/host + `vid` and /interface/wifi/provisioning + `radio-mac` still surface their `high` sections. Regression tests added for both halves: an explainCommand case where two pages tie on `high` and the linked one must win, and a fixture page that documents /interface/bridge so the tie exists at all. The lowercase fix to the census was incomplete — the commonality histogram still keyed raw names — so the step-3 figures were measured with a partly case-sensitive oracle. Corrected and regenerated throughout: scorable rows 3,223 -> 3,316 (72.3% of 4,587) conditional precision 76.1% -> 75.6% cascade 51.5/37.9/10.6 -> 50.1/37.2/12.7 zero-support pairs 26.6% -> 28.7% names at 26+ menus 21.0% -> 20.8% The support-gate comparison was also carrying scratch-script numbers, so the census now measures it directly: of the alignments naming alone would accept, the shipped gate keeps 87.2% and strict exclusivity 42.4% (was 84.6%/41.9%). The step-4 replay follows the new retention rule, so rescued rows read 148, not 236. CodeRabbit's recognized-zero-support case is now covered explicitly: a section whose names the command tree knows, where no named menu accepts any of them, must keep nothing — distinct from silence, which keeps everything. Part of #131, #58, #61
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
briefings/B-0024-command-prose-join.md (1)
410-414: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winState that acceptance cannot promote a row by itself.
“Nothing reaches
highon acceptance” and “never for a name accepted at 26+ menus” imply thatnamecan never be high. Lines 368-370 show thatnamecan be high when the section directly aligns with a menu. Qualify both statements with “from acceptance alone” or “from field existence alone.”Proposed wording
-Ubiquitous names cannot reach `high` on acceptance. +Ubiquitous names cannot reach `high` from acceptance alone. -never for a name accepted at 26+ menus +never from field existence alone, including names accepted at 26+ menusAlso applies to: 560-562
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@briefings/B-0024-command-prose-join.md` around lines 410 - 414, Qualify the statements in the “Field existence” and “Ubiquitous names” bullets, including the corresponding passage around the later repeated wording, to say that acceptance or field existence alone cannot promote a row to high. Preserve the distinction that direct section alignment with a menu can still make name high.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@briefings/B-0024-command-prose-join.md`:
- Around line 264-265: Reconcile every published census percentage and
explicitly state its denominator: in briefings/B-0024-command-prose-join.md
lines 264-265, change the unambiguous count to 1,446 / 3,316 = 43.6% (or
otherwise name its denominator); on lines 287-291 and 493-495, recalculate the
spurious-path percentage from 128 / 433 = 29.6%; on lines 569-572, change the
value to 1,271 / 4,587 = 27.7%; and in src/mcp.ts lines 799-801, identify that
200 / 468 is the property-owning-section denominator.
In `@src/eval/command-prose-join.ts`:
- Around line 404-419: Update the census grading loop around gradeRow and its
acceptsByName lookup to build acceptance data separately for each candidate
section_id, using that section’s own property names as gradeRows does via
acceptance.all(id). Pass the section-scoped acceptance map into acceptsName and
ensure gateNone, gateSupport, and gateExclusive use the same scoped data,
replacing the corpus-wide acceptsByName behavior while preserving existing
candidate and path handling.
- Around line 380-385: Update the linkedPageOf construction in
command-prose-join.ts to resolve each menu path using the same per-path DISTINCT
page_id lookup as lookupProperty, rather than relying on unordered table-scan
results. Query non-null page IDs for each path and use that result consistently
when populating linkedPageOf, preserving the existing downstream
linkContributes, modeled before-label, and oldHigh/oldLow behavior.
---
Outside diff comments:
In `@briefings/B-0024-command-prose-join.md`:
- Around line 410-414: Qualify the statements in the “Field existence” and
“Ubiquitous names” bullets, including the corresponding passage around the later
repeated wording, to say that acceptance or field existence alone cannot promote
a row to high. Preserve the distinction that direct section alignment with a
menu can still make name high.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 23e59177-e45e-4ec2-99ec-b3387bfe7b94
📒 Files selected for processing (9)
CHANGELOG.mdbriefings/B-0024-command-prose-join.mdsrc/eval/command-prose-join.tssrc/mcp.tssrc/menu-paths.tssrc/property-confidence.test.tssrc/property-confidence.tssrc/query.test.tssrc/query.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use Bun and TypeScript, including Bun-native runtime APIs such as
bun:sqlite,Bun.serve, andbunx.Rosetta uses Bun and TypeScript; prefer
bun,bun test, andmake verifywhere applicable, rather than Node/npm-oriented substitutes.
Files:
src/property-confidence.test.tssrc/mcp.tssrc/query.test.tssrc/menu-paths.tssrc/eval/command-prose-join.tssrc/property-confidence.tssrc/query.ts
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Rosetta is read-only documentation/schema context and must not connect to or modify a user's router.
Files:
src/property-confidence.test.tssrc/mcp.tssrc/query.test.tssrc/menu-paths.tssrc/eval/command-prose-join.tssrc/property-confidence.tssrc/query.ts
**/*.{ts,tsx,md}
📄 CodeRabbit inference engine (AGENTS.md)
Keep the attribution boundary visible when community RouterOS skills surface, because they are supplemental rather than official MikroTik documentation.
Files:
src/property-confidence.test.tsCHANGELOG.mdsrc/mcp.tssrc/query.test.tssrc/menu-paths.tsbriefings/B-0024-command-prose-join.mdsrc/eval/command-prose-join.tssrc/property-confidence.tssrc/query.ts
*
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Keep this Copilot instructions file short; put substantive rules in narrow instruction files under
.github/instructions/*.instructions.md.
Files:
CHANGELOG.md
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.md: Keep each project documentation file limited to its canonical role; prefer the documented canonical home instead of creating a new top-level Markdown file.
Do not duplicate content from this routing index; move operational detail, schema blocks, or long rule lists to their canonical documentation or instruction files.
Apply the repository's Markdown linting and fenced-code conventions, while excluding LLM instruction files where the repository explicitly requires that exclusion.
Files:
CHANGELOG.mdbriefings/B-0024-command-prose-join.md
CHANGELOG.md
📄 CodeRabbit inference engine (CLAUDE.md)
Record user-visible shipped changes in
CHANGELOG.mdunder[Unreleased]or release sections.
Files:
CHANGELOG.md
src/{query,mcp,browse}.ts
📄 CodeRabbit inference engine (AGENTS.md)
Route MCP, query, classifier, TUI, and canonicalizer behavior through shared core code, usually
src/query.ts; keepsrc/mcp.tsandsrc/browse.tsas thin adapters.
Files:
src/mcp.tssrc/query.ts
briefings/B-*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Store grounded research and decision support in
briefings/B-*.md.
Files:
briefings/B-0024-command-prose-join.md
🧠 Learnings (1)
📚 Learning: 2026-08-01T01:16:11.873Z
Learnt from: mobileskyfi
Repo: tikoci/rosetta PR: 134
File: src/mcp.ts:790-802
Timestamp: 2026-08-01T01:16:11.873Z
Learning: In src/property-confidence.ts and src/mcp.ts, treat documentation sections that cite a requested RouterOS menu but describe a different menu as cross-reference-only evidence and assign them medium, not high, confidence. The support gate must use the properties associated with the section's cited menus, preventing incidental mentions—such as /ip/firewall/filter in /interface/bridge/filter documentation—from qualifying for high confidence.
Applied to files:
src/mcp.tssrc/property-confidence.ts
🪛 LanguageTool
briefings/B-0024-command-prose-join.md
[style] ~290-~290: Consider an alternative for the overused word “exactly”.
Context: ...xcellent or entirely spurious, which is exactly the shape a support-ratio filter can ...
(EXACTLY_PRECISELY)
[style] ~354-~354: ‘vid’ is informal. Consider replacing it.
Context: ...page, which happens to document its own vid, so the lookup returned two medium r...
(VID)
[style] ~374-~374: Consider an alternative for the overused word “exactly”.
Context: ...ce, but it is still evidence, and it is exactly the right thing to break a tie the sect...
(EXACTLY_PRECISELY)
[style] ~427-~427: Consider simply using “of” instead.
Context: ...: when the command tree has never heard of any of the section's property names it cannot ...
(OF_ANY_OF)
🔇 Additional comments (10)
src/mcp.ts (1)
792-798: LGTM!CHANGELOG.md (1)
39-40: LGTM!briefings/B-0024-command-prose-join.md (1)
157-157: LGTM!Also applies to: 189-194, 206-211, 243-263, 266-286, 292-338, 340-409, 415-441, 486-493, 496-516, 556-559, 563-568, 573-574
src/eval/command-prose-join.ts (2)
32-39: LGTM!Also applies to: 68-75, 230-231
403-403: 📐 Maintainability & Code QualityNo shared
relatedhelper to move.
src/query.tsonly bindsrelatedas aSearchAllRelatedobject; it does not define the string ancestor/descendantrelatedhelper.src/menu-paths.ts (1)
33-38: LGTM!src/property-confidence.ts (1)
11-13: LGTM!Also applies to: 31-33, 92-119, 121-163, 168-201, 203-246, 248-267
src/property-confidence.test.ts (1)
104-120: LGTM!src/query.ts (1)
1082-1124: LGTM!Also applies to: 1126-1160
src/query.test.ts (1)
211-323: LGTM!Also applies to: 1119-1196
…nator `gradeRows` scores `SELECT DISTINCT name` for a section's properties; the census passed the raw list. A duplicated name is then weighted twice, which can flip which menu wins support — so the census was not measuring the shipped rule. It was also over-reporting the result: high labels surviving 1,119 (33.4%) -> 1,054 (31.5%) demoted to medium 2,230 -> 2,295 support-gate retention 87.2% -> 84.4% rescued rows 148 -> 153 Published figures corrected accordingly, in the briefing, the CHANGELOG and the property-confidence.ts header. Separately, several published percentages had been recomputed from a stale denominator or shipped without one. The census now prints the denominator at source — the M4 pair population, the unambiguous-alignment base, and the unscorable-row count as a fraction — rather than leaving it to be inferred, and the derived figures are regenerated: unambiguous alignments 1,446 (44.9%) -> 1,446 / 3,316 (43.6%) (section, path) pairs 433 -> 446 90-100% support 42.5% -> 41.3% names at one menu 36.9% -> 37.0% unscorable rows 1,271 (29.7%) -> 1,271 / 4,587 (27.7%) The MCP tool description now names its denominator too: 200 of the 468 documentation sections that carry properties, not 42.7% of all sections. Not changed: `linkedPageOf` resolving a menu's page without ORDER BY. Flagged as depending on table-scan order, but `commands.path` is `TEXT NOT NULL UNIQUE`, so at most one row exists per path and both the census and `lookupProperty` are deterministic. Verified on the artifact: 0 paths carry more than one page_id. Part of #131, #58, #61
Two threads of work on B-0024: measure whether a property's own section can serve as the missing command↔property key (step 3), then use the answer to recalibrate
routeros_lookup_property's confidence and ranking (step 4).Step 3 — Option A is a ranking signal, not a key
Measured on
v0.11.2-alpha.109(the CI release artifact viamake db-sync), census committed atsrc/eval/command-prose-join.ts— thedb-census.tsprecedent, not a CI gate.When a section names a path it is usually the right one; the dominant failure is silence, not misalignment — the opposite of the fuzzy page ranker's failure mode, which is why the two compose as tiers rather than one replacing the other.
Two sub-questions close outright:
sections,page_tables,page_table_rows, orproperties, onlysort_order. This corrects the briefing, which claimed those tables retain line spans.src/menu-paths.tsfactors extraction out oflink-commands.tsso census and linker cannot drift. That refactor is verified behaviour-preserving, not asserted: both versions run against separate DB copies,commands.page_iddiffed across all 41,967 rows — identical, and the 1,575 linked rows match the figure in #131.Step 4 — grade the row, not the query branch
highused to mean "the scoped branch found rows on the pagecommands.page_idlinks to" andlow"it didn't". The label described which SQL ran.src/property-confidence.tsnow scores each row against the requested menu:Unscoped lookups stay
mediumthroughout: with no menu to align to, the tier answers a different question.The candidate set had to move too
Grading alone is not enough, and the first cut of this PR got that wrong — caught in review. While the scoped branch still selected rows, the fuzzy link kept its veto:
/interface/bridge/hostlinks to the IGMP-snooping page, which documents its ownvid, so the lookup returned twomediumrows from that page and never graded thehighStatic Entries section that names/interface/bridge/hostoutright. Same for/interface/wifi/provisioning+radio-mac.Candidates now come from every page documenting the property, with page alignment per row. Two rules keep that from turning every lookup into a global dump:
That second rule fixes a regression the first cut introduced: tiers are menu-level, so
nameat/interface/ethernetis legitimatelyhighon Ethernet, Bonding and PoE-Out, and with ties broken by page titleexplainCommanddescribedname=ether2as "Name of the bonding interface".Measured
src/eval/command-prose-join.tsreplayslookupProperty's candidate set and filter and grades every candidate with the shippedgradeRow/supportedPaths, so these figures regenerate rather than being asserted. Over the 14,832 (menu, property-name) pairs the command tree says are real, 115,926 row labels (absent= not returned at all):low→lowabsent→absenthigh→mediumhigh→highlow→mediumlow→highabsent→highabsent→medium31.5% of the labels that shipped as
highsurvive; 1,940 rows escape a wrongly-lowlabel; 153 rows previously suppressed outright are now returned, 80 of themhigh.The support gate, and why the eval earned it
Whether a support ratio should gate
highwas step 4's open question. It must, and the retrieval eval proved it: without a gate,/ip/firewall/filter+actionscoredhighon the bridge firewall section, which cites that menu in a single sentence while documenting/interface/bridge/filter. Naming is not aboutness.The gate requires the requested menu to be among those accepting the most of the section's own property names, and distinguishes two zeroes: the command tree knowing none of the section's names is silence and cannot demote; knowing them and finding no named menu that takes any is evidence against every candidate. Of the alignments naming alone would accept, the gate keeps 84.4%; strict exclusivity (reject any section naming an unrelated menu) keeps 42.4% and discards correct alignments over incidental cross-references.
Effect on the motivating defects
/interface/bridge/port+pvid(#131)high, unrelated Apps rowlowand last/ip/dhcp-server+address-pool(#58)high, DHCPv6 and the ambiguous**Properties**demoted/interface/bridge/vlan+vlan-ids(#61)high— despitecommands.page_idstill pointing at the wrong page/interface/bridge/host+vidAll of these rank correctly; none is fixed at the source. The bad
commands.page_idjoin is still there — the grading stops it from mattering. HencePart of, notCloses.Eval fixture change — please read this one
src/eval/retrieval.ts's property assertion required ahighrow. Under the new contract that is stricter than intended: WireGuard'slisten-portsits in a bare property table that never names/interface/wireguard, so the correct top row is honestlymedium. It now requires the top-ranked row to be the expected page and notlow, and still requireshighwhere no page is expected.That second clause matters: it keeps
prop-firewall-filter-action(the known-failing BL-1 anchor) red. Without the support gate it had silently flipped green on the falsehighabove — a regression disguised as an improvement, caught only because the anchor exists.Not in scope
MENU_PATH_REnever matching bare top-level menus (~2.4pp coverage), andnormalizeMenuPathfabricating pseudo-paths — are recorded with measured cost and not fixed. The top-level fix changescommands.page_id, so it needs a before/after link diff, not unit tests.Verification
make typecheck,make lint,bun test(1,107 pass / 0 fail) green; retrieval eval against the synced release DB shows no regression and the same three known failures as baseline.Part of #131
Part of #58
Part of #61
Summary by CodeRabbit
New Features
Bug Fixes
Tests