* docs(specs): plan a template rule that shares templated positions Record the outcome, requirements, non-goals, plan and tasks for prefix-variance, an opt-in environment-manifest rule that templates a literal segment with the parameter another tool already uses at that position, so a session's writes to one entity share the read's template. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(analysis-view): share templated positions across manifest tools Add the opt-in prefix-variance template rule. It runs after the other opt-in rules and templates a literal at a position that another tool, of any method or segment count, templates by one of those rules after the same preceding parts, when the literal is one of that parameter's observed values or carries a digit and is spelled like one. The parameter takes the shared parameter's name, tools that now match merge, and a merged position keeps a tool's own rule when one templated it. A write to the one entity a session edited, such as PATCH /api/v1/collections/0RZ576QZQA5KG, becomes patch_collections_by_collection_id on the read's template instead of a literal single-entity tool. Tool naming moves to its own module, and the three comparing passes share the 32-segment split. Manifests that do not list the rule are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(analysis-view): drop the unused ManifestRule alias Parameters now type their rule as a format rule, a shared opt-in rule or prefix-variance, so nothing reads the union alias. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(specs): record the prefix-variance checks and corpus results Record the gates, and the 50 showcase captures with and without the rule: identical output without it, 25 write and 34 read tools templated with it, and the write tools the write-plan branch serves on top. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(analysis-view): share positions with digit-free generated identifiers prefix-variance accepted an unobserved literal only when it carried a digit, so a generated identifier that happened to hold none stayed a literal tool: an n8n workflow id such as FkMwxmXNGaGVsXFG, whose reads then never joined the item view. A literal now also qualifies when every value observed at the position has one length of eight or more characters and the literal has that length and uses only their character classes. Across the 50 showcase captures this templates 10 more read tools in Memos and n8n and no words. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(specs): record five environments rebuilt end to end with the rule Five randomly drawn environments rebuilt on VMs with the rule and the write-plan branch: smoke, retrieval and write tasks for two models, and the same calls against the live applications. Record the results, the misses and their causes, and the corpus numbers after the fixed-length clause. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(analysis-view): share a position with the parameter that observed the literal A shared position folded every donor's values into the earliest donor's parameter. With an opaque-token read before sibling-variance reads at the same position, a PATCH of /api/items/b2 qualified on the sibling's evidence but became /api/items/{uid} instead of joining the item read's /api/items/{item_id}. Each position now keeps one donor per naming rule with its own values, and a literal takes the donor that observed it, else the earliest one it is spelled like. Each donor's spelling (length and character classes of its values, and its fixed-length family) is summarised once per position, so a literal is checked in time independent of how many values were observed, rather than copying and rescanning them for every candidate. A family now forms from one observed value, as the requirement says, so a digit-free id of the same length shares a position that only one opaque-token or after-uid-literal tool supplied. The 50 showcase captures give the same tools as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(specs): record the prefix-variance review round Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(analysis-view): bound the spelling summary of shared positions spellingSummary classified every observed value with three full-size replaceAll copies and a set per distinct character, so a donor holding a multi-megabyte segment cost unaccounted memory and time in finish(). Characters are now classified in one pass, and a value longer than 128 characters takes no part in spelling comparisons: it shares a position only as an exactly observed value. The 50 showcase captures give the same tools. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
8.5 KiB
Verification
Verified base: 33b0643e on main 1629266e (the rule, the alias removal knip
asked for, and the fixed-length clause the end-to-end runs below led to). Executed on macOS with Node v22.23.1 (the repository pins
24.18.0 in .nvmrc, which was not installed).
Gates
npm run build:cached,npm run check:fast(typecheck and lint),npm run format:check,npx knip,npm run scan:todos,npm run docs:check,npm run verify:test-discoveryandnpm run mcp-catalog:check: passed.npm run jscpd: 3.49% duplicated lines over 3561 files with 3067 clones; main measures 3.49% with 3068, since the shared segment-limit split removes one clone.- oxlint reports no new finding on the changed files. The manifest accumulator keeps main's three warnings (file length, and the complexity and parameter count of its record append), one line longer.
npx vitest run --project domain --project services: 7119 passed, 5 pre-existing skips.npm run test:prepare, thennpm run test:focused --the analysis-view MCP and CLI boundaries, the compact input schema and tool schema validity boundaries, and the environment serve and export boundaries: 7 files, 76 passed.
New tests in environmentManifestView.test.ts cover:
- a literal shared across methods and depths, and the same capture without the rule;
- the spelling condition: same classes and length, case, missing digit, a colon word, a version;
- a digit-free member of a fixed-length identifier family, against a shorter word, a hyphenated value and a family shorter than eight characters;
- merges that keep a tool's own rule, including a sibling-variance parameter under a shared one;
- both halves of a templated pair;
- format-rule positions and the segment limit.
The showcase captures
The 50 showcase captures (45 HAR inspections and 5 capture_browser_scenario
captures) were projected with main's build and this branch's.
-
Without the rule, both builds give identical results for every capture: the manifest with no rules, the manifest with
after-uid-literal,opaque-token,sibling-varianceandsibling-pair-variance, and the environment-state view. 150 of 150 outputs hash equal. -
With
prefix-varianceadded toafter-uid-literal,opaque-tokenandsibling-variance, 69 literal tools in 10 captures are templated, 25 of them writes:- Komga: collection and readlist
PATCHandDELETE, book pages. - n8n: workflow
PATCH,DELETEand archive, and the reads of a workflow whose id holds no digit. - Memos: memo
PATCHandDELETE, user settings reads, and the reads of two memos whose ids hold no digit. - Navidrome: playlist
PUTandDELETE. - Mealie: recipe
PATCHandDELETE. - Grist: document
PATCHandDELETE, and table record writes. - Redpanda Console: subject and topic writes.
- Sentry: release
PUTandDELETE. - Shlink: short URL
PATCH. - Mattermost:
mereads, whichsibling-variancealready folds into the user parameter.
Tools go from 1591 to 1578, because Redpanda Console's per-subject reads and the per-memo and per-workflow reads merge.
- Komga: collection and readlist
-
Literals the rule leaves alone, as intended, include Espo CRM
layout, Federal Registerfacets, Ghostslug, Shlinkstats, LycheeMetrics::photoand Vault's path names. -
The environment-state view succeeds on every capture with the rule. Its types move to the templated collections (531 become 526 as literal ones merge); the one fewer item view is the Redpanda Console merge.
With the write plans of #5600 (its branch with this commit and main merged in, locally):
- Served write tools rise from 33 to 39: Komga collection create, update and delete; n8n workflow delete; Sentry release update and delete.
- Write tools with no state target fall from 102 to 87.
- The newly modelled tools that stay unserved are limited by the capture:
- no read after the write: Komga readlists, Mealie, Navidrome, Redpanda Console;
- n8n's item read shows an expanded
sharedrelation its create response never returned; - Shlink returns
tagssorted while the update echoes the request's order.
Five environments end to end
Five of the ten environments the rule changes were drawn at random, redrawing
until the draw held one whose new writes are served: Komga, Mealie, n8n,
Sentry and Shlink. Each was rebuilt on its own Linux VM with npm pack of
#5600, main and this branch merged locally, from the same scoped HAR as its
published version: manifest with prefix-variance, exported bundle, smoke
task, the generated retrieval tasks for gpt-oss-120b and GLM-5.3-Flash, and
a dry-run dataset. Where writes are served, hand-written write tasks ask for a
change in plain language and are graded from the environment's state through
data/get. The first n8n run found that a digit-free workflow id stayed
literal; the fixed-length clause fixed it, and n8n was rebuilt.
| Environment | Tools (published → rebuilt) | Served from state (reads / writes) | Retrieval gpt-oss | Retrieval GLM | Writes gpt-oss | Writes GLM |
|---|---|---|---|---|---|---|
| Komga | 42 → 46 | 26 / 3 | 12/12 (was 11/12) | 12/12 (11/12) | 3/4 | 4/4 |
| Mealie | 39 → 39 | 36 / 0 | 12/12 (12/12) | 12/12 (12/12) | – | – |
| n8n | 30 → 33 | 17 / 1 | 9/9 (9/9) | 9/9 (9/9) | 2/2 | 2/2 |
| Sentry | 34 → 34 | 26 / 3 | 12/12 (12/12) | 11/12 (12/12) | 2/4, rerun 0/4 | 4/4 |
| Shlink | 17 → 17 | 5 / 0 | 5/7, rerun 7/7 | 7/7 (7/7) | – | – |
Every smoke task passed. The misses:
- Sentry writes with gpt-oss: every failed episode passed
id: "sentry"for the/api/0/segment the tool schema describes asobserved: 0, so its calls addressed/api/sentry/…; every passing episode, and every GLM episode, passed0. The served tools work; the format-rule parameter for a segment that only ever held0invites the guess. - Komga update with gpt-oss and Shlink's first gpt-oss run: the model provider
rejected tool calls the model generated (
nullandundefinedfor optional query arguments) before the environment saw them. - Sentry
qa-03with GLM: the model never called the alert-rules tool.
The same calls were also replayed against the live applications, seeded by the same scripts (ids differ, so each side maps its ids to names):
- Komga 1.29.1, 9 of 12 steps equal: create, update and delete of a collection and the reads after them agree except for the generated id. The collection list after a create omits the new collection, because that list keeps its recorded membership, and the 404 for a deleted collection replays the recorded body with its path and timestamp.
- n8n 1.123.85, 3 of 8 steps equal: n8n refuses to delete a workflow that is not archived (400) and the environment deletes it; the workflow list replays its recorded response, so a delete does not show in it; archiving is not served. The read of the digit-free workflow failed in the first build (the state held only its list fields) and agrees in the second, apart from ids the seed generates.
After the review round
The branch merged main at 52c84f6f, then kept each shared position's values
with the parameter that observed them (one donor per naming rule, the literal
taking the donor that observed it, else the earliest it is spelled like),
summarised each donor's spelling once, and let one observation form a
fixed-length family, as the requirement states. Two new tests, a mixed-rule
position and a single observed workflow id, fail on the previous head and pass
here. The gates above passed again (domain and services 7255 passed with 5
pre-existing skips; the focused boundaries 76; jscpd 3.49%). Over the 50
showcase captures the tools with and without prefix-variance are unchanged,
name for name: 69 literal tools templated, 25 of them writes.
A second review round bounded the spelling summary: characters are classified
in one pass, and a value longer than 128 characters is compared only exactly.
Its test fails on the previous head, the gates passed again (domain and
services 7257, focused boundaries 76, jscpd 3.49%), and the census over the 50
captures is again unchanged.