Skip to Content

Tuning

GoToMesh never guesses how to classify a keyword. If no rule covers a keyword, the engine surfaces it as unresolved rather than inventing a section for it. So tuning a mesh isn’t a one-time setup — it’s a tight loop driven by what the plan tells you is missing.

This page is the practical guide: the plan says X is unresolved — which file do I edit? It consolidates the levers in the order you’d reach for them. (For the deeper why behind the shape of the config, see mental models.)

Every generated mesh also ships its own TUNING.md at the repo root — the same guidance, living next to the files it points at. This page is the hosted copy.

The tuning rhythm

gtmesh extract --cluster <id> # pull keyword data (once; immutable, re-plan freely) gtmesh plan # prints the plan to stdout (pipe to a file with `> plan.md` if you like)

In the printed plan, read two sections:

  • “Catalogue / create — by page type” — is the shape right? Too many of one type?
  • “Unresolved — N page(s) need a config rule” — each entry names a keyword with no section/page_type.

Then adjust the files below and re-plan. plan is read-only and free — iterate as much as you like; nothing is written until you apply. Unresolved is your to-do list: each entry is either a keyword pattern you haven’t mapped, or junk you should exclude.

The knobs, in the order to tune them

1. gtmesh.config.yamltaxonomy

The structural skeleton. Touch this first because everything else routes against it.

  • sections — your top-level URL areas (e.g. guides, compare, glossary).
  • clusters — grouping axes (category, topic). Used for siblings and cluster-based mesh links.
  • intentsorder matters. Classify picks the first intent present on a keyword, so put the most discriminating intents first. informational is near-universal — put it last, or it swamps the topic cluster. To override the global order for one section, set classification.section_intent (e.g. glossary: informational, so a “what is X” page isn’t tagged branded just because it names a brand).
  • tiers + authority — the funnel bands (A conversion → B feeder → D spoke) and how authority flows.
  • identity.anchorpage URL reads off the wrong keyword? head (default) names the slug after the page’s head keyword; parent_topic uses the source cluster label. A slug is frozen once committed (a head-flip on re-extract won’t churn the URL) — to deliberately move a URL, use a rename group-edit (it pins the new slug, keeps the page, and emits a 301).
  • Junk extra page from one stray keyword? Set classification.min_group_members: 2 (optionally min_group_volume_pct) to fold a below-threshold typed sibling back into the parent’s dominant page. Real fan-out with demand is kept.

2. reference/entities.csv

Your domain’s entities (brands, products, categories). This is load-bearing for the mesh: entity resolution sets a row’s category cluster, and a spoke links up to the hub for the same entity (e.g. a glossary page about X → the X hub). Make it comprehensive, or up-links won’t form.

When a keyword names more than one entity, the winner is chosen by classification.entity_class_priority (highest class first), then longest match — never by row order. So set that priority for your domain and order the CSV however you like.

An entity contributes a value to every taxonomy.clusters axis it has a column for — so a catalogue can give entities.csv a brand column (and declare a brand cluster axis), letting a product page roll up by brand as well as category.

3. gtmesh.config.yamlsections_map

Ordered when → then rules: a keyword pattern → a section. First match wins. A match value is a case-insensitive substring, or an anchored /regex/ for precision. Anything matching no rule → unresolved. This is the file you edit most when working down the unresolved list — add rules until what’s left is only genuine junk.

4. reference/signals.csv

Keyword pattern → role (hub / sub-hub / spoke) + tier (A / B / D). Reserve hub/A for true conversion pages — let the long tail be feeders and spokes. (Real Tier-A hubs usually come from seed-pages, below, not from keyword signals.) Role and tier are derived before page_type, so a page_type can require when: { role: hub }.

5. gtmesh.config.yamlpage_types

Each entry: a when (match by section and/or role/tier) → a template + schema. Ordered; first match wins. For any new type, author its templates/<type>.md (prose theory) + schemas/<type>.schema.yaml (structure). Use schema_variants + variant_when for variant-by-peer.

The per-type schemas (schemas/<type>.schema.yaml) are yours — extend or add freely; gtmesh upgrade never touches them. They share a base, schemas/common.schema.yaml, which is engine-owned and refreshed on upgrade (if you’ve edited it, the new version lands as common.schema.yaml.new to merge).

6. reference/scope.yaml + adapters.ahrefs.exclude_substrings

Filter out what you’ll never build pages for — navigational / brand-asset / off-topic queries (login, logo, retailer names, sizing charts…). They work at different stages:

  • exclude_substrings drops them at extract — saves API credits.
  • scope.yaml parks them to backlog at plan — recoverable.

Either way, scoped-out rows drop off the unresolved list.

7. reference/seed-pages.csv

Source-less pages — apex/pillar/conversion hubs with no keyword behind them. In v1 your Tier-A hubs live here (give each an intent so it clusters). These are the destinations the keyword long tail links up to.

8. gtmesh.config.yamlscoring + transforms

  • scoring.formula ranks rows into priority; scoring.exempt marks off-calendar rows (priority = null); commercial_bonus weights by CPC. (A SERP-classified commercial-layer landing is exempt — it ships by business priority, not volume, so money pages aren’t buried behind volume-ranked blog content. Inert for a non-commercial mesh.)
  • transforms/*.ts — pure, row-local derivations for deterministic project columns (e.g. a computed score band). None ship by default. Titles are not heremetaTitle / title / navTitle / metaDescription are writer-authored editorial meta (config.authored_meta); the LLM writes good titles, with rules in foundation/editorial.md. Only slug is deterministic.

9. gtmesh.config.yamlaeo (the citability gate)

The engine-owned review-gate skill scores every draft for AI-answer-engine citability. You tune the numbers here — dimensions[] (weights + per-dimension threshold), overall_threshold, and the answer_block word range — never by editing the skill (upgrade keeps it current).

The voice the gate pulls toward is foundation/voice.md; the banned words are reference/editorial-rules.yaml. The gate and the humanizer skill both read those, so retune voice there, not in the skills.

10. Images — foundation/art-direction.md + gtmesh.config.yamlimages

If a page type has image slots (e.g. hero, gallery), the article-writer authors art-direction briefs and the engine-owned image-director skill generates + places the files (an explicit step).

  • Tune the look in foundation/art-direction.md (registers, prompts, negative prompt, per-type briefs).
  • Tune the knobs in gtmesh.config.yaml images: (gen model, registers.<name>.ratio, per-register settings).

The register names in the two files must match. You never edit the skill.

Rubrics — gtmesh.config.yamlrubrics

For rubric-bearing page types (review / comparison / best-for), rubrics declares the scored dimensions the writer evaluates into the page’s rubric. Retune the axes here, never by editing the template. Keep them concrete and few (3–6) so pages stay comparable; a page type with no entry scores no rubric.

Then run the loop

Once plan looks right:

gtmesh apply # catalogue everything (previews + asks to confirm) gtmesh promote --section <x> # choose a batch to build (sets status; --dry-run previews; demote reverses) gtmesh apply # scaffold the promoted rows → writing gtmesh status # the worklist (pages awaiting a body) # → the article-writer skill fills content/<slug>/index.yaml, runs `gtmesh validate`, humanizer, review-gate gtmesh seal <slug> # writing → review (schema-valid + lint-clean only) gtmesh publish <slug> # → published

For what each of those steps and statuses means in full, see lifecycle & reconcile.

Guardrails

  • Never hand-edit registry/pages.csvapply / seal / promote / lifecycle are its only writers.
  • The pipeline is deterministic and LLM-free except the article-writer step.
  • Structural mesh links render as nav, not prose — they live in the registry, not the page YAML. Don’t write up/sibling links into the body. A relink is a deterministic restamp that touches only the hash — the page file is untouched.
  • Changing a content-determining field → rewrite (the writer reruns the body); changing the render projection (slug, mesh links, an in-projection column) → restamp (deterministic, no writer). That’s why the registry carries two hashes.
Last updated on