Skip to content

feat(explain): operator fill on the token partition seam (#290 B2) - #292

Merged
mobileskyfi merged 5 commits into
mainfrom
feat/explain-operator-fill
Aug 13, 2026
Merged

feat(explain): operator fill on the token partition seam (#290 B2)#292
mobileskyfi merged 5 commits into
mainfrom
feat/explain-operator-fill

Conversation

@mobileskyfi

@mobileskyfi mobileskyfi commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Closes #290. Bumps #289 B1 → B2 on #264.

591 proved the [0,bytes) partition behind --tokens with only comment + variable-* claimed (27.44%). This PR fills the first B2 hole — operator bytes — without widening the proof-only ExplainSpanClass contract (#289 scope: "not touched" + src/explain.ts header).

Seam#290 amendment: ExplainTokenClass was ExplainSpanClass | "unclassified" and buildTokens(analyzed, spans) had one claim source. Instead of adding another member to ExplainSpanClass, split the vocabularies (ExplainTokenClass = ExplainSpanClass | "operator" | "unclassified", provisional until 264 B5), factor residualRanges() out of the gap walk, and make buildTokens take an ordered list of fills — argument order is fill order (#290 design-decision 1). Cross-fill overlap is a hard throw (fail-closed); callers achieve impossibility by offering only residual. EV: e10 operators, citedEvidence seeds it when tokens carry that pass. spans vs tokens split documented where enforced (explainCommand call site + commands/explain/README.md).

Fillsrc/explain/operator-tokens.ts consumes operators.ts (grounded 26 ops, 14 precedence, PR #287, no re-probe) on the residual only — longest-match, word-bounded (and/or/in/any), with dot traps (1. variable, 1.2 IP, (.1) time, ../// second-byte) and slash second-of-//. Shares scanQuotedString, sorted/non-overlapping/fully-inside-residual invariant.

Abstentions — a byte that is structurally path or argument is left for the later fills. Three rules, each grounded on the corpus device oracle (parseil_results.il_text), added in 4525be2 after the first review round:

Abstention IL grounding
, / = - outside a ( ) group — ( opens an expression, [ opens a command substitution and { a block/array literal [ /system/identity/get value-name=name ] is (evl /system/identity/get value-name=name) — path separators + an argument separator, no division and no comparison node
glued immediately after an argument = (the value) in-interface-list=!LAN, .id=*2, oid=.1.3.6.1.2.1
glued into an argument name :foreach x in=$list is /foreach counter=$x with no (in …) node; security.authentication-types=wpa2-psk stays one name; .id renders as the single symbol $.id, never (. …)

The first rule has a measured cost, stated rather than hidden: find where name="x" inside [ … ] does lower to a real (= $name x) node, so 224 of the corpus's 1,259 bracket-interior = bytes are genuine comparisons that now stay unclassified — against 1,035 that were plain arg=value. A where-aware fill can take them back later; claiming all 1,259 is 82% wrong. Under-claiming is the recoverable direction: unclassified is a first-class answer (#289 B1), while an over-claim is permanent under fill order.

Census — re-derived ritual (explain:token-census → explain:token-census:readme → explain-token-census.test.ts pin): 1,426,731 bytes, classifiedBytes 391474 → 403516 (27.44% → 28.28%), totalTokens 46580 → 63416, operator 9418 spans / 12042 bytes. commands/explain/README.md GENERATED block + prose paragraph updated; both checks plus explain:operator-readme:check green.

Tests borrow 291 review lessons — operator-tokens.test.ts per-trap, explain-token-census.test.ts seam+fill-order.

Two-hop note: bytes where operator abstains at top-level stay unclassified now and later become path/arg — don't misread the later B2 diff as stealing.

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

Summary by CodeRabbit

  • New Features

    • Added operator-aware token classification for improved command explanations.
    • Included operator tokens and supporting evidence in --tokens output.
    • Added recognition for additional operator spellings, including xand, andx, and alnum.
  • Bug Fixes

    • Improved handling of operators near quoted text, variables, punctuation, nesting, dots, and slashes.
    • Prevented overlapping or incorrectly matched token ranges.
  • Documentation

    • Updated token census results and documented operator-token coverage.

The B1 partition (591) shipped a total, gapless [0,bytes) token
stream behind --tokens with only comment + variable-* claimed. B2
fills holes from existing analyzers without touching the
proof-only ExplainSpanClass / data.spans[] contract.

Seam: split the vocabularies (ExplainTokenClass =
ExplainSpanClass | "operator" | "unclassified", provisional until
B5), factor residualRanges() out of buildTokens, and make
buildTokens take an ordered list of fills — argument order IS fill
order (#290 design decision 1). Cross-fill overlap is a hard throw;
callers achieve impossibility by offering only residual. EV
operators (e10) added, citedEvidence now seeds it when tokens
carry that pass.

Fill: src/explain/operator-tokens.ts scans the residual left by
spans, longest-match over the grounded table (operators.ts +
lowered aliases, minus re-lexed <>), word-bounded for and/or/in/
any, with dot traps (1. variable, 1.2 IP, (.1) time, .. second
dot), slash second-of-//, and top-level conservatism for , / = -
(depth>0 inside parens/brackets only, -> allowed everywhere).
Quoted strings are skipped via shared scanQuotedString. Every
emitted span is sorted, non-overlapping, fully inside residual,
and carries class operator + ev e10.

Census: 1,426,731 bytes, 27.44% → 28.62% classified (+16,833
operator bytes), 46,580 → 72,264 tokens (+13,907 operator runs),
README block re-derived, both checks gated.

Tests borrow the review lessons from 291: direct residual
scanner traps, explainCommand masking, multi-fill overlap, and
fill-order conservatism.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 13, 2026 01:53
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@mobileskyfi, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 56 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 71554c3c-ecf8-482f-a88f-59485644dab6

📥 Commits

Reviewing files that changed from the base of the PR and between 7f7b2c0 and d776aeb.

📒 Files selected for processing (7)
  • GLOSSARY.txt
  • commands/explain/README.md
  • src/explain.ts
  • src/explain/operator-tokens.ts
  • test/fixtures/explain/tokens.json
  • test/unit/explain-token-census.test.ts
  • test/unit/operator-tokens.test.ts
📝 Walkthrough

Walkthrough

The explain token pipeline now computes residual ranges, fills them with operator tokens, validates multiple fills, and records operator provenance. The operator scanner handles literals, strings, boundaries, nesting, and punctuation cases. Tests and census documentation reflect the new token class.

Changes

Operator token fill

Layer / File(s) Summary
Token fill pipeline
src/explain.ts, commands/explain/README.md
buildTokens now supports ordered fills, residual ranges, per-fill validation, and overlap checks. Operator tokens use class "operator" and evidence identifier e10.
Residual operator scanner
src/explain/operator-tokens.ts
operatorSpans scans residual bytes with longest-match ordering, string masking, boundary checks, nesting rules, and literal guards.
Validation and census updates
test/unit/*, test/fixtures/explain/tokens.json, GLOSSARY.txt
Tests cover residual ownership, operator matching, integration, and evidence. The fixture, census, and glossary include operator data.

Estimated code review effort: 4 (Complex) | ~45 minutes

Mergeability Score: 🔵 Low · up to 7f7b2

The PR adds operator classification and currently performs that scan even when token output is not requested, which can add unnecessary processing to explain requests; the documentation also contains a broken source link. The change is mergeable with explicit owner awareness or follow-up for these bounded issues.

Possibly related issues

  • tikoci/centrs issue 289 — The PR extends the B1 token partition with the residual operator fill described by this issue.

Possibly related PRs

  • tikoci/centrs#224 — Introduced the token construction that this PR extends with operator fills and evidence.
  • tikoci/centrs#287 — Established the operator metadata and spellings consumed by the scanner.
  • tikoci/centrs#291 — Added the token partition and token class structures extended by this PR.

Sequence Diagram(s)

sequenceDiagram
  participant ExplainCommand
  participant BuildTokens
  participant OperatorSpans
  participant EvidenceCollection
  ExplainCommand->>BuildTokens: existing spans and residual ranges
  BuildTokens->>OperatorSpans: residual ranges
  OperatorSpans-->>BuildTokens: operator tokens with e10
  BuildTokens-->>ExplainCommand: combined token result
  ExplainCommand->>EvidenceCollection: token provenance
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes claim operator bytes, preserve span contracts, enforce ordered residual fills, handle scanner traps, add evidence, and update census data as required by #290.
Out of Scope Changes check ✅ Passed The source, tests, fixture, and documentation changes directly support the B2 operator-fill objectives in #290.
Title check ✅ Passed The title clearly identifies the main change: adding operator filling to the explain token partition.
Description check ✅ Passed The description is detailed and covers the linked issues, behavior change, implementation, validation, assumptions, tests, and known limitations.
✨ 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 feat/explain-operator-fill

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@commands/explain/README.md`:
- Around line 1220-1223: Fix the `buildTokens` source link in the README to use
`../../src/explain.ts` instead of the path that traverses above the repository
root.

In `@src/explain.ts`:
- Around line 1129-1136: In the token-building flow around residualRanges,
operatorSpans, and buildTokens, only compute residual0 and opSpans when
options.tokens === true. Keep fills and buildTokens inside that branch, and
return undefined without scanning analyzed documents when tokens are not
requested.
🪄 Autofix

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: df6d5868-2f02-46c6-be2c-d415488b3eb0

📥 Commits

Reviewing files that changed from the base of the PR and between 54695ff and 7f7b2c0.

📒 Files selected for processing (7)
  • GLOSSARY.txt
  • commands/explain/README.md
  • src/explain.ts
  • src/explain/operator-tokens.ts
  • test/fixtures/explain/tokens.json
  • test/unit/explain-token-census.test.ts
  • test/unit/operator-tokens.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: CHR smoke (stable)
  • GitHub Check: Corpus census
  • GitHub Check: copilot-pull-request-reviewer
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (6)
{docs/**,.github/instructions/**,.github/**/*.yml,.github/**/*.yaml,**/*.{md,txt,dict}}

📄 CodeRabbit inference engine (AGENTS.md)

Run bun run lint:ci when changing documentation, instructions, security configuration, spelling dictionaries, or workflow files.

Files:

  • GLOSSARY.txt
  • commands/explain/README.md
test/fixtures/**/*

📄 CodeRabbit inference engine (test/AGENTS.md)

Keep fixtures under test/fixtures/ with clear source/provenance notes.

Files:

  • test/fixtures/explain/tokens.json
commands/*/README.md

📄 CodeRabbit inference engine (commands/AGENTS.md)

commands/*/README.md: Each commands/<name>/README.md is the executable specification's designed tier: document intent, flags, and behavior; include a Designed, not implemented table for spec-only flags; do not duplicate implemented-flag tables generated in docs/CLI.md; link to docs/CONSTITUTION.md rather than restating constitution-wide rules; document only command-specific behavior; and keep the Status line consistent with docs/MATRIX.md.
Implemented flags must be generated from CliCommandMetadata into docs/CLI.md via bun run docs:cli; command READMEs must not maintain duplicate tables of implemented flags.

Files:

  • commands/explain/README.md
commands/*/{README.md,examples.md}

📄 CodeRabbit inference engine (AGENTS.md)

Read the target command's README.md and examples.md as the executable specification before writing code or tests.

Files:

  • commands/explain/README.md
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/AGENTS.md)

src/**/*.{ts,tsx}: Use Bun-native TypeScript and Web APIs where possible
Errors must be actionable for humans and agents, with next-step guidance when a dependency, protocol, credential, or validation source is missing

Files:

  • src/explain/operator-tokens.ts
  • src/explain.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Transport or RouterOS-touching code is not done until bun run test:integration passes.
Do not silently fall back to another protocol when the caller pinned --via.
Do not make generated output the hand-edited source of truth.
Do not disable validation to make a test pass; validation is part of the product.

Files:

  • src/explain/operator-tokens.ts
  • test/unit/operator-tokens.test.ts
  • test/unit/explain-token-census.test.ts
  • src/explain.ts
🧠 Learnings (21)
📓 Common learnings
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 287
File: test/unit/explain-operators.test.ts:320-360
Timestamp: 2026-08-12T19:55:35.352Z
Learning: For the RouterOS operator sweep, `test/fixtures/explain/operators.json` stores captures from RouterOS 7.21.5 long-term, 7.23.3 stable, and 7.24rc4 testing. `scripts/explain-operator-slice.ts` compares every published sweep axis, including unary precedence, across these captures and emits `sweep.versionDifferences`. The `any` operator has no version differences in these captures.
📚 Learning: 2026-08-01T22:09:14.680Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 195
File: GLOSSARY.txt:548-561
Timestamp: 2026-08-01T22:09:14.680Z
Learning: In this repository, CSpell accepts a digit-suffixed token such as `toip6` when its alphabetic base token, `toip`, is present in `GLOSSARY.txt`. Do not add redundant digit-suffixed glossary entries solely for CSpell coverage.

Applied to files:

  • GLOSSARY.txt
📚 Learning: 2026-08-05T20:06:21.299Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 217
File: src/explain/symbols.ts:0-0
Timestamp: 2026-08-05T20:06:21.299Z
Learning: In `src/explain/symbols.ts`, comparative corpus measurements that explain a semantic modeling trade-off are rule provenance and follow the established F1/F2 documentation convention. Treat run-specific bookkeeping, fuzz counts, scratch-script paths, method pointers, and per-version missed-tail details as status data that should not remain in source comments.

Applied to files:

  • test/fixtures/explain/tokens.json
  • commands/explain/README.md
  • src/explain/operator-tokens.ts
  • test/unit/operator-tokens.test.ts
  • test/unit/explain-token-census.test.ts
  • src/explain.ts
📚 Learning: 2026-08-12T19:55:35.352Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 287
File: test/unit/explain-operators.test.ts:320-360
Timestamp: 2026-08-12T19:55:35.352Z
Learning: For the RouterOS operator sweep, `test/fixtures/explain/operators.json` stores captures from RouterOS 7.21.5 long-term, 7.23.3 stable, and 7.24rc4 testing. `scripts/explain-operator-slice.ts` compares every published sweep axis, including unary precedence, across these captures and emits `sweep.versionDifferences`. The `any` operator has no version differences in these captures.

Applied to files:

  • commands/explain/README.md
  • src/explain/operator-tokens.ts
  • test/unit/operator-tokens.test.ts
  • test/unit/explain-token-census.test.ts
  • src/explain.ts
📚 Learning: 2026-08-12T19:54:53.875Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 287
File: scripts/explain-operator-census.ts:429-454
Timestamp: 2026-08-12T19:54:53.875Z
Learning: In the centrs census commands, a successful fixture-drift check is valid only when the resolved corpus is the pinned corpus. `scripts/explain-operator-census.ts` rejects `--check` when `resolution.warning` is set. `scripts/explain-value-census.ts` has the same warning-only resolution shape but was deliberately left unchanged in PR `#287` to keep the RouterOS operator-surface work scoped to issue `#255`.

Applied to files:

  • commands/explain/README.md
  • src/explain/operator-tokens.ts
  • test/unit/operator-tokens.test.ts
  • test/unit/explain-token-census.test.ts
  • src/explain.ts
📚 Learning: 2026-08-06T14:17:50.227Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 224
File: commands/explain/examples.md:11-16
Timestamp: 2026-08-06T14:17:50.227Z
Learning: For the phase-1 offline explain API, offline behavior is tested through unit and fixture tests rather than CHR-backed integration tests. In `test/unit/explain-envelope.test.ts`, the CLI-spelling gate-versus-analysis behavior described by `commands/explain/examples.md` example `1b` and the resolved-menu behavior described by example `18b` have focused unit coverage. The CLI `explain` command surface is deferred and is not implemented as `src/cli/explain.ts` in this PR.

Applied to files:

  • commands/explain/README.md
  • src/explain/operator-tokens.ts
  • test/unit/operator-tokens.test.ts
  • test/unit/explain-token-census.test.ts
  • src/explain.ts
📚 Learning: 2026-08-01T23:46:54.392Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 197
File: src/explain/verbsplit.ts:383-387
Timestamp: 2026-08-01T23:46:54.392Z
Learning: In `src/explain/verbsplit.ts`, `VerbSplit` is the ratified Q6 boundary shape. Do not add document-scale context-certainty metadata to it independently; add that signal with the planned `ambiguous`/`unknown` verdict-vocabulary and phase-1 envelope work tracked in GitHub issue `#192`. Until then, `resolveVerbs` may correctly return root-based resolved paths for context-independent statements after context loss, but callers cannot observe that certainty was lost.

Applied to files:

  • commands/explain/README.md
  • src/explain.ts
📚 Learning: 2026-08-06T05:38:01.223Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 222
File: src/explain/defects.ts:1-70
Timestamp: 2026-08-06T05:38:01.223Z
Learning: In the TypeScript `src/explain` modules, references to phase-0 lab questions and issue numbers are an established documentation idiom when they explain the technical basis for a current contract. Keep these rationale references when they support implementation behavior. Remove dated decisions, delivery status, and future roadmap text from implementation and test comments.

Applied to files:

  • commands/explain/README.md
  • test/unit/explain-token-census.test.ts
  • src/explain.ts
📚 Learning: 2026-08-06T05:37:57.342Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 222
File: src/explain/pathresolve.ts:895-901
Timestamp: 2026-08-06T05:37:57.342Z
Learning: In `src/explain/pathresolve.ts`, over-depth defects must use `regionIn(loc, start, end)` rather than `spanIn` when the scanned extent can be empty. `regionIn` widens empty bracket or block-body extents to the enclosing statement span, preserving the `Defect` contract that `end > start` without fabricating a precise byte location.

Applied to files:

  • src/explain/operator-tokens.ts
  • test/unit/operator-tokens.test.ts
  • test/unit/explain-token-census.test.ts
  • src/explain.ts
📚 Learning: 2026-08-09T00:35:47.021Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 241
File: src/explain/transport.ts:0-0
Timestamp: 2026-08-09T00:35:47.021Z
Learning: In `src/explain/transport.ts`, transport input arguments must use `ExplainArguments` from `src/explain.ts`, narrowed with `Extract<ExplainArguments, { read: true }>`. Do not substitute `ArgumentsRead` from `src/explain/args.ts`: it is a distinct lexer-level type that includes `args: Record<string, string>` and uses a different span representation, while `src/explain.ts` keeps command arguments separately in `command.args`.

Applied to files:

  • src/explain/operator-tokens.ts
  • test/unit/explain-token-census.test.ts
  • src/explain.ts
📚 Learning: 2026-08-06T05:38:13.259Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 222
File: test/fixtures/explain/defects.json:3-73
Timestamp: 2026-08-06T05:38:13.259Z
Learning: In `src/explain/pathresolve.ts`, `Loc.base = -1` widening applies to defects raised during nested walker traversal, such as `over-depth`, when their local offsets cannot be mapped safely from UTF-16 string indexes to analyzed UTF-8 byte offsets. Defects emitted by the top-level segmenter are already in analyzed-document byte space and therefore retain precise offsets even when their enclosing statement contains non-ASCII text. The behavior is covered by `test/fixtures/explain/defects.json` and `test/unit/explain-defects.test.ts`.

Applied to files:

  • src/explain/operator-tokens.ts
  • test/unit/operator-tokens.test.ts
  • test/unit/explain-token-census.test.ts
  • src/explain.ts
📚 Learning: 2026-08-06T05:37:57.693Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 222
File: src/explain/defects.ts:1-70
Timestamp: 2026-08-06T05:37:57.693Z
Learning: In TypeScript modules under src/explain, retain references to phase-0 lab questions and issue numbers when they document the technical rationale for current implementation behavior or contracts. Remove comments containing dated decisions, delivery status, or future roadmap information when it does not explain current behavior, including in tests.

Applied to files:

  • src/explain/operator-tokens.ts
📚 Learning: 2026-08-05T03:19:54.280Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 209
File: test/unit/explain-menus.test.ts:146-152
Timestamp: 2026-08-05T03:19:54.280Z
Learning: In `test/unit/explain-write.test.ts`, keep separate test anchors for `isDanglingBarePath` and menu-table rejection. Known menu paths such as `/ip/firewall/filter` and `/log` reach `isDanglingBarePath` at document end, while `/system/reboot` is rejected earlier by `isConfirmedNav` because `isMenuPath` returns false. Do not combine these inputs in one test when asserting coverage of either mechanism.

Applied to files:

  • test/unit/operator-tokens.test.ts
  • test/unit/explain-token-census.test.ts
📚 Learning: 2026-08-09T05:23:19.794Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 242
File: src/explain/values.ts:83-90
Timestamp: 2026-08-09T05:23:19.794Z
Learning: In `src/explain/values.ts`, RouterOS time literals are order-independent and additive on RouterOS 7.23.3 and 7.24rc3. The `isTimeShape` function must use full fragment coverage rather than enforce descending time-unit order or reject repeated units. Examples such as `1s1m`, `1m1m`, `1h1h1h`, and `1s1ms` are valid `time` values.

Applied to files:

  • test/unit/operator-tokens.test.ts
  • test/unit/explain-token-census.test.ts
📚 Learning: 2026-07-03T21:35:09.559Z
Learnt from: CR
Repo: tikoci/centrs PR: 0
File: commands/api/AGENTS.md:0-0
Timestamp: 2026-07-03T21:35:09.559Z
Learning: Applies to commands/api/src/protocols/adapter.ts : Map REST `.query` words exactly to native `?` words without the `?` prefix, including eq, comparisons, has-property, absence, and stack operators; multiple words without an explicit stack op are implicitly AND-ed.

Applied to files:

  • test/unit/operator-tokens.test.ts
📚 Learning: 2026-08-09T00:35:51.834Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 241
File: src/explain/transport.ts:0-0
Timestamp: 2026-08-09T00:35:51.834Z
Learning: In `src/explain/transport.ts`, offline `print where` transport classification must fail closed unless each query expression is an attribute token with a valid property name and a non-empty decoded value. The runtime-exercised Q8 shape is `name=value`; bare property names, empty values, infix comparisons such as `address>1.1.1.1`, REST comparison words such as `>name=value`, and repeated `where` tokens must not produce a REST `.query` request.

Applied to files:

  • test/unit/operator-tokens.test.ts
  • src/explain.ts
📚 Learning: 2026-08-10T23:41:13.189Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 256
File: src/explain/args.ts:936-945
Timestamp: 2026-08-10T23:41:13.189Z
Learning: In `src/explain/args.ts`, `pushArrayMembers` must withdraw the enclosing array shape with `DEPTH_BOUND_REACHED` when `depth >= MAX_MEMBER_DEPTH`; returning `null` incorrectly reports the unverified literal as a valid array. CHR 7.23.3 accepts valid array nesting to at least depth 64, while invalid `(1,)` members remain syntax errors at all tested depths. The 948-script corpus has a maximum observed array-member depth of 6, so the depth-8 analysis bound does not drop observed valid corpus literals.

Applied to files:

  • test/unit/operator-tokens.test.ts
📚 Learning: 2026-08-06T21:57:06.372Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 226
File: test/integration/cli-smoke.test.ts:333-347
Timestamp: 2026-08-06T21:57:06.372Z
Learning: For the `centrs explain` CLI, ambient stdin is read only when no positional argument can provide the offline input. When a positional input is present and stdin is supplied, the explain result includes the `usage/stdin-ignored` warning. In `test/integration/cli-smoke.test.ts`, no-input coverage must test both child stdin shapes: no `stdin` option (`/dev/null`) and `stdin: ""` (an empty pipe).

Applied to files:

  • test/unit/operator-tokens.test.ts
📚 Learning: 2026-08-06T14:17:38.964Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 224
File: commands/explain/README.md:0-0
Timestamp: 2026-08-06T14:17:38.964Z
Learning: For the `explain` command documentation, `commands/explain/examples.md` specifies the finished target contract, not the current implementation phase. `commands/explain/README.md` contains the phase inventory and must map omitted phase fields to the issues and examples that will make them green. Future phase gaps must add fields without changing the finished example contract.

Applied to files:

  • test/unit/explain-token-census.test.ts
📚 Learning: 2026-06-11T06:18:36.306Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 13
File: commands/transfer/examples.md:5-9
Timestamp: 2026-06-11T06:18:36.306Z
Learning: In the `centrs` project (tikoci/centrs), `commands/transfer/examples.md` uses a multi-prefix example numbering scheme: `1-21` for core REST examples, `N*` for native-api, `S*` for SSH/SFTP, and `P*` for pending/gated examples. The test↔example mapping is NOT positional (not "example N → assertion N"). Instead, `test/integration/transfer.test.ts` annotates each test block with its example ID and records coverage via `recordIntegrationEvidence({ exampleIds })`. This is the intended, unambiguous mapping mechanism for this project.

Applied to files:

  • test/unit/explain-token-census.test.ts
📚 Learning: 2026-08-02T01:55:57.600Z
Learnt from: mobileskyfi
Repo: tikoci/centrs PR: 197
File: src/explain/pathresolve.ts:0-0
Timestamp: 2026-08-02T01:55:57.600Z
Learning: In `src/explain/pathresolve.ts`, `isUnreadablePath` identifies unreadable menu paths in both relative and absolute spelling. However, document-context poisoning applies only to unreadable `/`-led statements (`isUnreadableAbsolute`); relative unreadable paths do not poison document context by declared design.

Applied to files:

  • src/explain.ts
🔇 Additional comments (2)
src/explain.ts (1)

108-108: 🎯 Functional Correctness

Run the required RouterOS integration suite.

Run bun run test:integration before merge. This changed RouterOS-touching TypeScript code is not complete until that command passes.

As per coding guidelines: **/*.{ts,tsx}: Transport or RouterOS-touching code is not done until bun run test:integration passes.

Source: Coding guidelines

commands/explain/README.md (1)

1209-1223: 📐 Maintainability & Code Quality

Run the required documentation lint.

Run bun run lint:ci after updating this README.

As per coding guidelines: {docs/**,.github/instructions/**,.github/**/*.yml,.github/**/*.yaml,**/*.{md,txt,dict}}: Run bun run lint:ci when changing documentation, instructions, security configuration, spelling dictionaries, or workflow files.

Source: Coding guidelines

Comment thread commands/explain/README.md Outdated
Comment thread src/explain.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds the first B2 “operator” fill to the --tokens gapless token partition for explain, consuming the grounded operator surface and enforcing explicit multi-fill ordering without expanding the proof-only spans[] vocabulary.

Changes:

  • Introduces src/explain/operator-tokens.ts to claim operator bytes on the residual (longest-match + trap rules) and cite e10 only when operator tokens exist.
  • Refactors token building to accept an ordered list of fills (buildTokens(..., fills)) and factors residualRanges() out for reuse/testing.
  • Updates corpus census fixture + README docs and adds unit tests for operator traps, residual seam, and fill-order safety.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/explain.ts Adds ordered-fill token construction, exports residualRanges(), extends token class with operator, and wires operator fill + evidence citation.
src/explain/operator-tokens.ts New residual-only operator scanner implementing longest-match, word-boundary, dot/slash traps, and depth conservatism.
test/unit/operator-tokens.test.ts Adds focused tests for operator scanner traps and end-to-end behavior via explainCommand --tokens.
test/unit/explain-token-census.test.ts Adds seam + fill-order tests for residualRanges() and multi-fill buildTokens.
test/fixtures/explain/tokens.json Updates the pinned corpus census numbers and introduces operator counts/bytes.
commands/explain/README.md Updates token-census prose to reflect the operator fill and the split between spans[] and tokens[].
GLOSSARY.txt Adds test-vocabulary tokens used by the operator-fill unit tests.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/explain/operator-tokens.ts Outdated
Comment thread src/explain/operator-tokens.ts Outdated
Comment thread src/explain/operator-tokens.ts Outdated
Comment thread commands/explain/README.md Outdated
mobileskyfi and others added 4 commits August 12, 2026 19:02
#292)

CodeRabbit:
- commands/explain/README.md: `../..//../src/explain.ts` traversed above
  repo root — fix to `../../src/explain.ts` (also covers the duplicate
  Copilot thread on same line).
- src/explain.ts: `residualRanges` + `operatorSpans` ran on every
  `explainCommand` even when `options.tokens` was not set and `tokens`
  was discarded. Gate the whole fill — residual + scan + buildTokens —
  on `options.tokens === true`; future B2 fills belong inside the same
  branch so the residual work is pay-for-what-you-ask.

Copilot:
- src/explain/operator-tokens.ts:112 comment said "single quote" while
  handling `"` — fix to "double-quoted string".
- src/explain/operator-tokens.ts:202 hot-loop `[",","/","=","-"].includes`
  allocated a fresh array per spelling attempt — hoist to a module
  constant `TOP_LEVEL_CONSERVATIVE` Set.
- src/explain/operator-tokens.ts:222 `out.sort` was redundant: `i` only
  moves forward so `out` is already by `start`. Return `out` directly
  and avoid O(n log n) on every corpus census scan.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…es (#290)

Three false-positive classes the bot rounds did not reach, each grounded on
the corpus device oracle (`parseil_results.il_text`) rather than argued.
Together they were 4,489 of the fill's 13,907 corpus claims (32%).

1. `[` was counted as expression grouping alongside `(`, so the `, / = -`
   conservatism switched OFF inside command substitution — the one place it
   is most needed. Inside `[ … ]` the IL shows a COMMAND:
   `[ /system/identity/get value-name=name ]` is
   `(evl /system/identity/get value-name=name)` — path separators and an
   argument separator, no division and no comparison node. Measured on the
   corpus: 1,225 `/` (path roots, path separators, CIDR masks such as
   `dst-address=0.0.0.0/0`), 1,105 `=` (`to=json`, `output=user`, `bpm=150`),
   907 `-` (`global-config-overlay`, `default-name`, `back-to-home`) and 231
   `,` were claimed as operators. Replace the depth counter with a delimiter
   stack: only `(` opens an expression; `[` is a command and `{` a block or
   array literal.

2. A spelling glued immediately after an argument `=` is inside the VALUE —
   `in-interface-list=!LAN` (108), `admin-mac=*…`/`.id=*2` (49),
   `oid=.1.3.6.1.2.1` (17).

3. A spelling glued into an argument NAME is a name byte. `:foreach x in=$list`
   is `/foreach counter=$x` in the IL with no `(in …)` node anywhere — 234 of
   the 376 `in` claims were this. Corpus-wide, 121 scripts contain `in=` and
   exactly one has an `(in …)` node. Same for the dotted names: the IL keeps
   `security.authentication-types=wpa2-psk` as one name and renders `.id` as
   the single symbol `$.id`, never `(. …)` — 193 claims.

The first abstention has a measured cost, stated rather than hidden:
`find where name="x"` inside `[ … ]` DOES lower to a real `(= $name x)` node,
so 224 of the 1,259 bracket-interior `=` bytes are genuine comparisons that now
stay `unclassified`, against 1,035 that were plain `arg=value`. A `where`-aware
fill can take them back later; claiming all 1,259 is 82% wrong. Under-claiming
is the recoverable direction — `unclassified` is a first-class answer (#289 B1)
and a later fill can still claim the byte, while an over-claim is permanent
under fill order.

Census re-derived through the ritual: `classifiedBytes` 408307 → 403516
(28.62% → 28.28%), `operator` 13907 → 9418 spans and 16833 → 12042 bytes.
The PR still moves B1's number (27.44% → 28.28%) — it moves it by less, on
bytes that are actually operators.

`test/unit/operator-tokens.test.ts` asserted `[a,b]` → `[","]` with the comment
"Inside brackets (command substitution)", so the defect was pinned as intended
behavior; that row is now the opposite assertion plus per-rule coverage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The worked example for the next B2 fill put `pathSpansOnResidual` *after*
`opSpans`, which inverts #290 design decision 1 — the whole point is that
`pathresolve.ts` has claimed the `/` and `args.ts` the `=` before the operator
scanner runs, so it only ever sees bytes nobody else wanted.

That ordering is also what lets the operator fill's three abstentions relax:
they exist because the fills that own those bytes do not run yet. A fill
inserted after the operator scanner would leave them permanently load-bearing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…]` end to end

Two gaps in #290's own seam tests:

- "buildTokens with ordered fills concatenates in argument order" asserted a
  throw and nothing about order. Fill order decides who may CLAIM a byte, not
  the emitted order — the partition is always sorted by `start`. Rename to what
  is true and assert it: the same two fills either way round produce the same
  stream, byte-ordered, with the trailing gap `unclassified` on `e1`.
- The conservatism test covered `:put 1,2` vs `:put (1,2)` but never a bracket,
  which is where the fill was actually wrong. Add the `explainCommand`-level
  case: a whole `[ /ip/route/find where … ]` line yields zero operator tokens.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mobileskyfi

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

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.

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.

explain/tokens: B2 — the operator fill (consumes #255's grounded table)

2 participants