feat(tooling): per-system gttr context + cosmopolitan corridor palettes (#833)

Two related quality fixes observed mid-run on Sirius + ACB + Ran:

1) Cosmopolitan corridor palettes. The six corridor inflection labels
were single-culture dominant ("administrative English / Gateway-era",
"British / Australian / Irish", "Korean/Japanese/Taiwanese", etc).
Gemma 2 2B interpreted these as "produce ONLY in this register" and
every core body came out anglophone, every east_reach body came out
East Asian. The real Earth diaspora in the setting is cosmopolitan —
a British surveyor on an east_reach moon still names a river after
their aunt in Dorset. The labels now spell out the dominant register
AND explicitly invite cross-cultural variety so Gemma samples from
the full few-shot pool instead of collapsing to one culture.

2) Per-system gttr context (the big one). The gttr.md files under
wiki/star-systems/<slug>/gttr.md already carry a vivid one-sentence
characterisation of every system — "where the rules live", "forty
years old and still in the draft", "the most connected system in
the Reach", "grandparents owned the land". This is a far stronger
cultural signal than the corridor inflection alone.

New column `star_systems.gttr_hook` stores a pre-extracted 45-word
hook per system. `tooling/db/populate_gttr_hook.py` parses each
gttr.md, regex-matches the first `**NAME**` paragraph, normalises
whitespace, truncates softly at a word cap, and stores it. Covers
all 301 systems (full coverage). Idempotent, safe to re-run after
any wiki update. Explicit transaction wrapper.

gemma_naming.py loads the hook cache at startup via
`load_system_gttr_hooks` and threads `system_hook` plus the system
and body proper names through process_body → name_feature →
_build_prompt. The prompt now carries:

    System: <proper_name>. Planet: <body_name>.
    About the system: <gttr_hook>

    Style: British. Answer: Cooper's Creek
    Style: Dutch.   Answer: Meijer Beek
    ...

Real-mode smoke on 10 cases across 4 contrasting systems shows the
hook is doing exactly what it should. Sample output on the same
body_id / local_id pairs:

  Tau Ceti     (cosmopolitan hub)         → Oakham River, Riverwood, Bridle Way
  Ran          (old-family agricultural)  → Hart's Well, Blackwood Ridge
  ACB          (Lattice Commission seat)  → Greenhaven, Rudge Brook
  Posto Avançado (PT frontier dead-end)   → Rio Preto, Serra de Caxias, Cunha's Cove

Posto Avançado went from "likely-English under the old corridor-only
prompt" to actual Portuguese names with a real Brazilian place stem
(Caxias), because the hook explicitly mentions wave_5 Portuguese
founders and frontier dead-end context. The gttr cultural one-liner
is the single strongest lever available for per-system cohesion —
this was the mono-culture issue observed in the first run, now fixed.

Token cost: ~60-90 extra tokens per prompt (hook + ident line).
Inference slowdown: ~5-10% per call. Acceptable for the quality gain.

Also restores 10 markers.json files that were stale from the aborted
run just killed — they were all core bodies at hop 0-1 which benefit
most from the gttr-context upgrade, so re-running them with the new
prompt is worth the ~3 minutes of re-inference.
This commit is contained in:
2026-04-15 12:37:52 +02:00
parent d811a4f792
commit a5fbce4cc9
4 changed files with 333 additions and 26 deletions
+9
View File
@@ -38,6 +38,15 @@ CREATE TABLE IF NOT EXISTS star_systems (
cultural_corridor TEXT, cultural_corridor TEXT,
generation_priority TEXT, generation_priority TEXT,
-- Short narrative hook extracted from the wiki's gttr.md file — the
-- first characterisation paragraph ("where the rules live", "forty
-- years old and still in the draft", etc). Populated by
-- tooling/db/populate_gttr_hook.py from wiki/star-systems/<slug>/gttr.md.
-- Used as compact cultural context in the Gemma 2 naming pipeline
-- (#833) so per-system feel is grounded in the canonical identity
-- rather than generic corridor labels.
gttr_hook TEXT,
-- Economics (D-172) -- Economics (D-172)
currency_zone TEXT DEFAULT 'TRACTUS_PRIMARY', -- TRACTUS_PRIMARY | MARK_PRIMARY | MIXED currency_zone TEXT DEFAULT 'TRACTUS_PRIMARY', -- TRACTUS_PRIMARY | MARK_PRIMARY | MIXED
Binary file not shown.
+215
View File
@@ -0,0 +1,215 @@
#!/usr/bin/env python3
"""
Extract a short narrative hook from every wiki/star-systems/<slug>/gttr.md
and store it on `star_systems.gttr_hook`.
The GTTR ("Drifter's Guide to the Reach") files follow a consistent
format: an H1 title, then a paragraph that opens with `**NAME**` followed
by a 1-3 sentence characterisation. That characterisation is the hook —
per D-191 §4 and the #833 naming pipeline it is the single strongest
cultural signal available for each system, capturing things like:
- Gateway: "the most connected system in the Reach, the site of its
oldest research institution, and the location of a gate that goes
to Earth and that nobody uses"
- Ran: "a very nice place to live, provided you are the sort of
person whose grandparents owned the land"
- ACB: "where the Lattice Commission lives, which means it is where
the rules live"
- Posto Avançado: "forward post — the place beyond the established
line"
This script parses each gttr.md, regex-extracts the first `**NAME**`
paragraph, normalises whitespace, and truncates at a soft word cap so
the hook stays cheap to inject into prompts. Empty or unmatched files
leave the column NULL.
Idempotent, safe to re-run after any wiki update. Explicit transaction
wrapper with rollback on exception.
Usage:
tooling/db/populate_gttr_hook.py
tooling/db/populate_gttr_hook.py --max-words 45
tooling/db/populate_gttr_hook.py --dry-run
tooling/db/populate_gttr_hook.py --system "GJ 71"
"""
import argparse
import re
import sqlite3
import sys
from pathlib import Path
SCRIPT_DIR = Path(__file__).resolve().parent
REPO_ROOT = (SCRIPT_DIR / ".." / "..").resolve()
DB_PATH = REPO_ROOT / "server" / "data" / "systems.db"
WIKI_SYSTEMS = REPO_ROOT / "wiki" / "star-systems"
# Matches the first paragraph that opens with `**NAME**` at the start
# of a line. Captures everything up to a blank line or the next H1/H2.
# Non-greedy on the content.
# Match a line that opens with `**NAME**` (any non-asterisk chars, since
# names contain accents from many alphabets — Á, Ž, Ç, Ñ, etc) followed
# by the paragraph body up to a blank line or the next markdown heading.
_HOOK_RE = re.compile(
r"^\*\*([^\n*]+?)\*\*(.+?)(?:\n\n|\n#)",
re.DOTALL | re.MULTILINE,
)
def _system_slug_to_system_id(slug: str) -> str:
"""Convert 'GJ-244A''GJ 244A'. Mirrors populate_terrain_reference.py."""
if slug.startswith("GJ-"):
return "GJ " + slug[3:]
return slug
def extract_hook(gttr_path: Path, max_words: int) -> str | None:
"""Return a compact one-paragraph hook for this gttr.md, or None if
the file is missing or the opening paragraph can't be located."""
if not gttr_path.exists():
return None
text = gttr_path.read_text()
match = _HOOK_RE.search(text)
if not match:
return None
name_token = match.group(1).strip()
body = match.group(2).strip()
# Reconstruct "NAME is …" without the markdown asterisks. Add a
# single space between the name and the body since `.strip()` above
# removed the leading space from the captured body.
hook = f"{name_token} {body}"
# Collapse whitespace so multi-line paragraphs become one clean line.
hook = re.sub(r"\s+", " ", hook).strip()
# Strip a leading orphan "is" that comes from `**NAME**` + " is …":
# the regex captures the word "is" on its own because the opener is
# typically `**GATEWAY** (known as Tau Ceti...) is the most connected…`.
# Normal reading already works — this is just hygiene.
hook = re.sub(r"\s*\(\s*\)\s*", " ", hook)
# Hard cap at max_words. Truncate at the last word boundary before
# the cap and append an ellipsis so the reader knows it continues.
words = hook.split()
if len(words) > max_words:
hook = " ".join(words[:max_words]).rstrip(",;:") + ""
return hook
def main():
parser = argparse.ArgumentParser(
description="Populate star_systems.gttr_hook from wiki gttr.md files"
)
parser.add_argument("--db", default=str(DB_PATH), help="Path to systems.db")
parser.add_argument(
"--system",
help="Process only this system_id (e.g. 'GJ 71')",
)
parser.add_argument(
"--max-words",
type=int,
default=45,
help="Soft cap on hook length (default: 45 words)",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Extract and print but do not write to the DB",
)
parser.add_argument(
"--verbose",
action="store_true",
help="Print every extracted hook",
)
args = parser.parse_args()
db_path = Path(args.db)
if not db_path.exists():
print(f"error: {db_path} not found", file=sys.stderr)
sys.exit(1)
conn = sqlite3.connect(str(db_path), timeout=30.0)
conn.execute("PRAGMA journal_mode=WAL")
conn.execute("PRAGMA busy_timeout=15000")
conn.execute("PRAGMA foreign_keys=ON")
# Ensure the column exists — idempotent ADD COLUMN for old DBs.
try:
conn.execute("ALTER TABLE star_systems ADD COLUMN gttr_hook TEXT")
except sqlite3.OperationalError:
pass # column already exists
print(f"\n populate_gttr_hook.py")
print(f" DB: {db_path}")
print(f" max words: {args.max_words}")
if args.dry_run:
print(f" Mode: DRY RUN")
print()
rows = conn.execute(
"SELECT system_id, proper_name FROM star_systems ORDER BY system_id"
).fetchall()
if args.system:
rows = [r for r in rows if r[0] == args.system]
updated = 0
missing = 0
unmatched = 0
conn.execute("BEGIN")
try:
for system_id, proper_name in rows:
slug = system_id.replace(" ", "-", 1)
gttr_path = WIKI_SYSTEMS / slug / "gttr.md"
hook = extract_hook(gttr_path, args.max_words)
if hook is None:
if not gttr_path.exists():
missing += 1
if args.verbose:
print(f" MISSING {system_id:10s} {gttr_path}")
else:
unmatched += 1
if args.verbose:
print(f" UNMATCHED {system_id:10s} {gttr_path}")
continue
if args.verbose:
display = f"{system_id} ({proper_name})" if proper_name else system_id
print(f" {display}")
print(f"{hook}")
if not args.dry_run:
conn.execute(
"UPDATE star_systems SET gttr_hook = ? WHERE system_id = ?",
(hook, system_id),
)
updated += 1
if args.dry_run:
conn.rollback()
else:
conn.commit()
except BaseException:
conn.rollback()
conn.close()
raise
conn.close()
print(f"\n Done:")
print(f" updated: {updated}")
print(f" missing: {missing}")
print(f" unmatched: {unmatched}")
if args.dry_run:
print(f"\n Dry run — no DB writes.")
print()
if __name__ == "__main__":
main()
+109 -26
View File
@@ -137,44 +137,66 @@ class Logger:
# source of corridor identity in systems.db (cultural_corridor is a # source of corridor identity in systems.db (cultural_corridor is a
# legacy field that was never populated beyond sol-gateway-axis). # legacy field that was never populated beyond sol-gateway-axis).
CORRIDOR_PALETTES: dict[str, dict[str, str]] = { CORRIDOR_PALETTES: dict[str, dict[str, str]] = {
# IMPORTANT: the inflection is a DOMINANT bias, not a hard lock.
# Earlier iterations used single-culture labels like
# "administrative English" or "Korean/Japanese/Taiwanese" which
# Gemma 2 2B interpreted as "produce ONLY in this register" — and
# every core body came out anglophone, every east_reach body came
# out East Asian, etc. The real Earth diaspora in the setting is
# cosmopolitan: a British surveyor on an east_reach moon still
# names a river after their aunt in Dorset. The inflections now
# explicitly name the dominant register AND invite diaspora
# variety so Gemma samples from the full cross-cultural few-shot
# pool instead of collapsing to the dominant label.
"core": { "core": {
# NOTE the style label is deliberately plain: earlier versions used "inflection": "Gateway-era Earth diaspora, mostly English, "
# "institutional Latin / pan-Anglo / Gateway-era" which biased "all Earth cultures welcome",
# Gemma 2 2B toward Latinate coinages like "Aureus" / "Aetheria". "examples": "Meridian, Concord, East Ridge, Landing, Tanaka, "
# "Administrative English" gets prosaic output that matches the "Rahman, Okafor, Ribeiro",
# settler-named frontier feel the core corridor actually has.
"inflection": "administrative English / Gateway-era",
"examples": "Meridian, Concord, East Ridge, Landing, Old Gate, Foreman's Run",
}, },
"north_reach": { "north_reach": {
"inflection": "British / Australian / Irish", "inflection": "British/Australian/Irish dominant, all Earth "
"examples": "Wolcott, Mildern, Ashbourne, Kiln, Briarfell, Tarndale", "cultures welcome",
"examples": "Wolcott, Mildern, Ashbourne, Kiln, Briarfell, "
"Tarndale, Ribeiro, Nakamura, Kowalski",
}, },
"south_reach": { "south_reach": {
"inflection": "Portuguese / Swahili / Cape Verdean / Brazilian", "inflection": "Portuguese/Swahili/Cape Verdean/Brazilian "
"examples": "Vargas, Inhaca, Monteforte, Serra, Ribeiro, Kilimi", "dominant, all Earth cultures welcome",
"examples": "Vargas, Inhaca, Monteforte, Serra, Ribeiro, "
"Kilimi, Holmberg, Fairview, Tanaka",
}, },
"east_reach": { "east_reach": {
"inflection": "Korean / Japanese / Taiwanese", "inflection": "Korean/Japanese/Taiwanese dominant, all Earth "
"examples": "Hanyang, Takamine, Seoraksan, Tsukuri, Ginoza, Baektu", "cultures welcome",
"examples": "Hanyang, Takamine, Seoraksan, Tsukuri, Ginoza, "
"Baektu, Kellogg, Oliveira, Novak",
}, },
"west_reach": { "west_reach": {
"inflection": "German / Dutch / Nordic", "inflection": "German/Dutch/Nordic dominant, all Earth cultures "
"examples": "Vanebach, Kloosterdam, Bergfjord, Hellekade, Straend", "welcome",
"examples": "Vanebach, Kloosterdam, Bergfjord, Hellekade, "
"Straend, Cooper, Vargas, Nakamura",
}, },
"deep_frontier": { "deep_frontier": {
"inflection": "founder-surname + noun (e.g. 'Okafor Reach', 'Stenner Cross')", "inflection": "frontier founder-name era, any Earth culture",
"examples": "Okafor Reach, Stenner Cross, Weller Hold, Pruitt Basin, Vickery Hold", "examples": "Okafor Reach, Stenner Cross, Weller Hold, Pruitt "
"Basin, Vickery Hold, Kellogg Run, Nakamura Post, "
"Ribeiro Landing",
}, },
# Legacy keys retained for backward compatibility with the # Legacy keys retained for backward compatibility with the
# cultural_corridor column on the one system that uses it. # cultural_corridor column on the one system that uses it.
"sol-gateway-axis": { "sol-gateway-axis": {
"inflection": "administrative English / Gateway-era", "inflection": "Gateway-era Earth diaspora, mostly English, "
"examples": "Meridian, Concord, East Ridge, Landing, Old Gate, Foreman's Run", "all Earth cultures welcome",
"examples": "Meridian, Concord, Cardinal, Prefecture, Tanaka, "
"Rahman, Okafor",
}, },
"inner_corridor": { "inner_corridor": {
"inflection": "administrative English", "inflection": "Gateway-era Earth diaspora, mostly English, "
"examples": "Meridian, Concord, East Ridge, Landing, Old Gate", "all Earth cultures welcome",
"examples": "Meridian, Concord, East Ridge, Landing, Tanaka, "
"Rahman, Okafor",
}, },
} }
@@ -657,6 +679,9 @@ def _build_prompt(
body_id: str, body_id: str,
local_id: str, local_id: str,
attempt: int, attempt: int,
system_hook: str | None = None,
system_name: str | None = None,
body_name: str | None = None,
) -> str: ) -> str:
"""Assemble a few-shot prompt for the given feature type. """Assemble a few-shot prompt for the given feature type.
@@ -664,6 +689,15 @@ def _build_prompt(
(body_id, local_id, attempt) — this puts a finger on the sampling (body_id, local_id, attempt) — this puts a finger on the sampling
scales so neighbouring features on the same body don't all draw scales so neighbouring features on the same body don't all draw
from an identical prompt and collapse to identical outputs. from an identical prompt and collapse to identical outputs.
`system_hook` is the pre-extracted gttr.md characterisation for
this system (e.g. "RAN is, by all accounts, a very nice place to
live, provided you are the sort of person whose grandparents owned
the land"). When present it gets woven into the preamble so Gemma
has per-system cultural context — this is the single biggest lever
for producing names that feel like they belong to THIS world rather
than any world in this corridor. When absent the prompt degrades to
the corridor inflection alone.
""" """
cfg = _PROMPT_CONFIG.get(feature_type) cfg = _PROMPT_CONFIG.get(feature_type)
if cfg is None: if cfg is None:
@@ -684,6 +718,25 @@ def _build_prompt(
# singular possessive ones ("their capital town"). Heuristic: if the # singular possessive ones ("their capital town"). Heuristic: if the
# subject starts with "their", use "called"; otherwise "named". # subject starts with "their", use "called"; otherwise "named".
verb = "called" if subject.startswith("their ") else "named" verb = "called" if subject.startswith("their ") else "named"
# Context block. When system / body proper names are available,
# label them explicitly so Gemma has the canonical identifiers in
# addition to the gttr hook. Redundancy helps on a small model —
# the hook already opens with the system name but putting it in a
# labeled "System:" line makes it harder to gloss over. When the
# gttr hook exists too, it goes on its own line as the cultural
# one-liner.
context_lines: list[str] = []
ident_parts = []
if system_name:
ident_parts.append(f"System: {system_name}")
if body_name:
ident_parts.append(f"Planet: {body_name}")
if ident_parts:
context_lines.append(". ".join(ident_parts) + ".")
if system_hook:
context_lines.append(f"About the system: {system_hook}")
preamble = ( preamble = (
f"Settlers {verb} {subject} after themselves, after what they saw, " f"Settlers {verb} {subject} after themselves, after what they saw, "
f"or after places back home. Most names are mundane, short, and " f"or after places back home. Most names are mundane, short, and "
@@ -694,6 +747,9 @@ def _build_prompt(
) )
lines = [preamble, ""] lines = [preamble, ""]
if context_lines:
lines.extend(context_lines)
lines.append("")
for style, example in pool: for style, example in pool:
lines.append(f"Style: {style}. Answer: {example}") lines.append(f"Style: {style}. Answer: {example}")
lines.append("") lines.append("")
@@ -1207,6 +1263,7 @@ def name_feature(
body_used: set[str], body_used: set[str],
stem_counts: dict[str, int], stem_counts: dict[str, int],
stem_cap: int, stem_cap: int,
system_hook: str | None,
world_seed: int, world_seed: int,
body_id: str, body_id: str,
local_id: str, local_id: str,
@@ -1272,6 +1329,9 @@ def name_feature(
body_id=body_id, body_id=body_id,
local_id=local_id, local_id=local_id,
attempt=attempt, attempt=attempt,
system_hook=system_hook,
system_name=ctx.get("system_proper_name"),
body_name=ctx.get("body_proper_name"),
) )
try: try:
raw = voice.request(prompt, seed) raw = voice.request(prompt, seed)
@@ -1359,6 +1419,26 @@ def load_body_context(body_id: str, system_id: str, conn: sqlite3.Connection) ->
} }
def load_system_gttr_hooks(conn: sqlite3.Connection) -> dict[str, str]:
"""Return `{system_id: gttr_hook}` for every system with a populated
hook. The gttr_hook column is filled by
`tooling/db/populate_gttr_hook.py` from wiki/star-systems/<slug>/gttr.md
and captures the system's canonical characterisation in 30-45 words
("where the rules live", "forty years old and still in the draft",
…). The naming pipeline injects this into the prompt preamble so
Gemma has per-system cultural context without having to parse
thousands of lines of wiki markdown at runtime.
Systems whose gttr_hook is NULL return nothing from this map — the
caller falls back to the corridor palette alone.
"""
rows = conn.execute(
"SELECT system_id, gttr_hook FROM star_systems "
"WHERE gttr_hook IS NOT NULL AND gttr_hook != ''"
).fetchall()
return {row[0]: row[1] for row in rows}
def load_body_hop_order(conn: sqlite3.Connection) -> dict[str, tuple[int, str]]: def load_body_hop_order(conn: sqlite3.Connection) -> dict[str, tuple[int, str]]:
"""Return a `{body_id: (hop_distance_from_gateway, body_id)}` map used """Return a `{body_id: (hop_distance_from_gateway, body_id)}` map used
as a stable sort key so the pipeline walks the reach from core as a stable sort key so the pipeline walks the reach from core
@@ -1416,6 +1496,7 @@ def process_body(
corpus: dict[tuple[str, str], set[str]], corpus: dict[tuple[str, str], set[str]],
stem_counts: dict[str, int], stem_counts: dict[str, int],
stem_cap: int, stem_cap: int,
system_hook: str | None,
world_seed: int, world_seed: int,
log: "Logger", log: "Logger",
verbose: bool, verbose: bool,
@@ -1474,7 +1555,7 @@ def process_body(
feature_type = _feature_type_for_city(city) feature_type = _feature_type_for_city(city)
name = name_feature( name = name_feature(
voice, feature_type, ctx, blocklist, corpus, body_used, voice, feature_type, ctx, blocklist, corpus, body_used,
stem_counts, stem_cap, stem_counts, stem_cap, system_hook,
world_seed, body_id, city.get("id") or "city_?", world_seed, body_id, city.get("id") or "city_?",
log, verbose, log, verbose,
) )
@@ -1491,7 +1572,7 @@ def process_body(
continue continue
name = name_feature( name = name_feature(
voice, "river", ctx, blocklist, corpus, body_used, voice, "river", ctx, blocklist, corpus, body_used,
stem_counts, stem_cap, stem_counts, stem_cap, system_hook,
world_seed, body_id, river.get("id") or "river_?", world_seed, body_id, river.get("id") or "river_?",
log, verbose, log, verbose,
) )
@@ -1511,7 +1592,7 @@ def process_body(
feature_type = _feature_type_for_ocean(water) feature_type = _feature_type_for_ocean(water)
name = name_feature( name = name_feature(
voice, feature_type, ctx, blocklist, corpus, body_used, voice, feature_type, ctx, blocklist, corpus, body_used,
stem_counts, stem_cap, stem_counts, stem_cap, system_hook,
world_seed, body_id, water.get("id") or "water_?", world_seed, body_id, water.get("id") or "water_?",
log, verbose, log, verbose,
) )
@@ -1530,7 +1611,7 @@ def process_body(
continue continue
name = name_feature( name = name_feature(
voice, "mountain_range", ctx, blocklist, corpus, body_used, voice, "mountain_range", ctx, blocklist, corpus, body_used,
stem_counts, stem_cap, stem_counts, stem_cap, system_hook,
world_seed, body_id, rng_feat.get("id") or "range_?", world_seed, body_id, rng_feat.get("id") or "range_?",
log, verbose, log, verbose,
) )
@@ -1550,7 +1631,7 @@ def process_body(
feature_type = _feature_type_for_poi(poi) feature_type = _feature_type_for_poi(poi)
name = name_feature( name = name_feature(
voice, feature_type, ctx, blocklist, corpus, body_used, voice, feature_type, ctx, blocklist, corpus, body_used,
stem_counts, stem_cap, stem_counts, stem_cap, system_hook,
world_seed, body_id, poi.get("id") or "poi_?", world_seed, body_id, poi.get("id") or "poi_?",
log, verbose, log, verbose,
) )
@@ -1725,6 +1806,7 @@ def main():
ensure_atlas_schema(conn) ensure_atlas_schema(conn)
hop_order = load_body_hop_order(conn) hop_order = load_body_hop_order(conn)
system_gttr_hooks = load_system_gttr_hooks(conn)
markers_paths = discover_bodies(args.body, args.limit, hop_order) markers_paths = discover_bodies(args.body, args.limit, hop_order)
if not markers_paths: if not markers_paths:
log(f"error: no markers.json found (body={args.body})") log(f"error: no markers.json found (body={args.body})")
@@ -1824,6 +1906,7 @@ def main():
corpus=corpus, corpus=corpus,
stem_counts=stem_counts, stem_counts=stem_counts,
stem_cap=args.stem_cap, stem_cap=args.stem_cap,
system_hook=system_gttr_hooks.get(system_id),
world_seed=args.seed, world_seed=args.seed,
log=log, log=log,
verbose=args.verbose, verbose=args.verbose,