Something went wrong. Try again.
Native control, inspection, UI automation and evidence capture for a running Witcher 3 process.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862"""Offline command contracts shared by argparse, help and agent discovery.
Examples are shell arguments after ``w3dev``. They are parsedin tests, never executed. Keep completion promises separate from side effects."""import textwrap
from .arguments import BUTTONS, SPECS, ROOT, number, choice
PREFIX = 'w3dev 'CONTRACT_VERSION = 1CATALOG = {}
def command(name, summary, arguments, requires, effects, returns, examples): CATALOG[name] = dict(name=name, summary=summary, arguments=arguments, prerequisites=requires, effects=effects, completion=returns, examples=examples)
LIVE = 'One attached live game with matching process/build. Mutations require no unresolved uncertainty.'READY = LIVE + ' Loaded player; use wait-ready after loading.'PAUSED = READY + ' Pause using call pause before this operation.'CLOSED = 'Game closed; existing local deployment receipts and hash-owned installed files.'UI = LIVE + ' Instrumented UI root available. Resume simulation for normal menu interaction.'
command('call control', 'Switch human/agent control through the native bridge.', 'human|agent.', LIVE, 'Cancels native agent input/ticks; human restores controller pass-through and removes devkit pause/time scale. Agent enables virtual input and pauses a loaded world.', 'Fresh snapshot with control and physical_controller_passthrough. Prefer debug control for readback verification.', ['call control human'])command('call heal', 'Refill the current living Geralt to his native maximum health.', 'No arguments.', 'Debugger owner only; use debug heal. Alive loaded Witcher, agent mode, devkit pause, no-save protection and no pending input/ticks.', 'Calls the existing SetHealth(GetMaxHealth()) once when injured; no revival, stepping, control switch or maximum-health change.', 'Before/after health, maximum before/after and full_health_verified. debug heal additionally verifies a fresh snapshot.', ['call heal'])for name,arguments,example in ( ('overlay_status','No payload.','call overlay_status'), ('overlay_config','on|off|clear plus x|y HUD coordinates.','call overlay_config "off|24|210"'), ('overlay_set','generation|global-or-actor-id|title|rows-joined-by-tilde|accent.','call overlay_set "7|global|Example|Intent: Approach|normal"'), ('overlay_list','generation|actor-id|title|profile|row_count|rows-joined-by-tilde|accent. Safe ASCII; title <=32, profile <=64, 0..12 rows <=64 each. Explicit row_count preserves empty rows; only this command permits up to 1024 payload characters.','call overlay_list "7|1|Example|Profile v1|2|Approach~|normal"'), ('overlay_list_clear','No payload; clears only the expanded list.','call overlay_list_clear'), ('overlay_remove','generation|actor-id.','call overlay_remove "7|1"')): command('call '+name,'Passive debug overlay: '+name+'.',arguments, 'Use debug overlay; mutations require the session owner.', 'Only owned HUD display/data changes; no input, pause, world advancement or model policy.', 'Overlay enabled/attached state, render counter, error and generation-scoped publications.', [example])CATALOG['call overlay_list']['prerequisites'] += ( ' Already selected alive actor (Geralt counts), current generation, at most 3 selected enemies. ' 'Clear the active list explicitly before publishing for a different actor.')CATALOG['call overlay_list']['completion'] += ( ' Rejected updates preserve the previous publication unchanged. No auto-selection or gameplay effects. ' 'Renderer version 3 shows all 12 rows, each on two 32-character lines without ellipsis.')
command('verify', 'Verify installed dependencies against local ownership receipts.', 'No arguments.', 'Local deployment receipts and installed game.', 'Read files only; no game requests.', 'build and verified_files; fails on the first missing or changed dependency.', ['verify'])
command('reconcile-game-update', 'Check and reconcile an explicitly supported Steam executable update.', 'Default: read-only plan. --apply installs only reviewed native adapter bytes and records the checked executable. ' '--rollback ARTIFACT restores one retained transaction, never the executable; mutually exclusive with --apply.', CLOSED + ' No active owner/startup or uncertain delivery. Existing hash-owned installation and exact reviewed profile required. ' 'Build matching candidates with python3 native/build.py before applying.', 'Under the installation lock, checks exact executable and Steam build, stock scripts, import hooks, WNB registration/strings ' 'and dependency hashes. Apply retains old adapters/receipt before writes; never modifies game executable, saves, scripts, HUD or settings.', 'Unsupported builds reject; no arbitrary expected-hash override. Static checks do not establish live ABI compatibility: ' 'consult the profile qualification report. Rollback leaves updated Steam executable intact and may deliberately block launch. ' 'After interruption inspect the retained transaction; do not rewrite receipts.', ['reconcile-game-update', 'reconcile-game-update --apply'])command('doctor', 'Inspect local setup without launching or contacting the game.', 'No arguments.', 'None; works from a source-only clone.', 'Read paths, receipts, hashes and process identity only.', 'checks with ok/detail; failed required checks make ok=false. Session status is informational.', ['doctor'])command('launch', 'Enable lab mode and request a DX12 development launch through Steam.', 'No arguments.', CLOSED + ' KDE KWin with Desktop 2, dbus/gi and local background-adapter compiler; unchanged owned settings/rules.', 'Verifies dependencies, records personal display options, enables 1920x1080 windowed/background mode and pins Desktop 2 before starting Steam/game with -net -debugscripts. Keeps the current desktop visible. stop automatically restores personal settings after exit.', 'state=launch_requested and lab_mode setup receipts, NOT engine readiness. Wait for the actual game window before attach; background-status/workspace-status verify actual mapping/placement. Setup/Steam errors attempt restoration; a live game blocks unsafe removal. If startup never completes or the game exits outside stop, close it and run reset-game-settings.', ['launch'])command('attach', 'Create a session for the running game and acquire its no-save lock.', 'No arguments.', 'Verified installation; exactly one game process with the real game window, not the splash. No existing live session.', 'Records save hashes/settings copies and session identity; hello activates bridge/no-save protection.', 'Bridge snapshot envelope. Does not load a save. Use call ui and call saves next.', ['attach'])command('status', 'Read the last session record, including unresolved mutations.', 'No arguments.', 'A local native-session.json from an earlier attach.', 'Read disk only; no liveness check.', 'historical=true and session; a record does NOT prove that the game is running.', ['status'])command('wait-ready', 'Wait for an attached world to become ready.', 'No arguments; deadline 180 seconds.', LIVE, 'Poll snapshots only; never requests another load.', 'Ready snapshot envelope, or timeout. Does not require a new generation.', ['wait-ready'])command('load', 'Load an exact save and wait for a new ready world generation.', 'filename: exact name returned by call saves; do not guess or use a filesystem path.', LIVE, 'Loads once; discards current unsaved world state, cleans devkit test state.', 'Ready snapshot in a NEW generation, within 180 seconds. Invalidates old actor references.', ['load manualsave_52586_7ea4bc00_5c355be'])command('input', 'Deliver bounded gameplay input and verify script-tick completion.', 'args: move_x|move_y|look_x|look_y|button|ticks. Four finite axes [-1,1]; ticks integer 2..120. ' 'Buttons: ' + ', '.join(BUTTONS) + '. Quote the whole payload; use -- before a payload starting with minus.', PAUSED, 'Resumes for the requested script ticks, releases input and re-pauses. Movement/camera use native XInput.', 'before/after snapshots, ticks_observed, input_delivered=true, effect_verified=false. Delivery is NOT a landed hit. Early player death releases input, pauses, and reports terminal=player_death with observed/requested ticks.', ["input '0|0|0|0|AttackLight|60'", "input -- '-1|0|0|0|None|30'"])command('advance', 'Advance a paused world by a bounded number of script ticks.', 'ticks: integer 1..600.', PAUSED, 'Resumes and re-pauses simulation without delivering an input action.', 'before/after and ticks_observed. Early player death cancels the remainder and reports terminal plus observed/requested ticks. No deterministic GPU/render-frame guarantee.', ['advance 30'])command('capture', 'Save a desktop screenshot and accompanying world snapshot.', 'No arguments.', LIVE + ' spectacle available.', 'Writes PNG and JSON beneath artifacts/<session>/.', 'path, snapshot, capture_scope=desktop, same_render_frame=false; may include other windows.', ['capture'])command('stop', 'Exit the lab, restore personal settings and audit save files.', 'No arguments.', LIVE, 'Requests engine exit once; waits up to 15 seconds, then compares saves, finalizes capture and restores performance configuration plus personal display/focus/workspace settings. Independent cleanup steps are attempted even if another fails. Does not restore any saves.', 'state=stopped, save_files_unchanged and game_settings restoration result. A failed audit is a failure even if the game exited. Cleanup errors retain completed results in native-stop.json; use reset-game-settings/perf-restore after resolving conflicts. No settings removal while the game remains alive.', ['stop'])command('recover', 'Acknowledge uncertainty and allow subsequent mutations.', 'No arguments.', LIVE + ' First inspect status, call snapshot, events and relevant artifacts.', 'Clears the local uncertainty flag only. Does NOT undo, replay, repair or verify the old operation.', 'ok=true, replayed=false. If runtime compilation is unknown, restore/restart and verify before proceeding.', ['recover'])command('evaluate', 'Compile and invoke a trusted WitcherScript function body once.', 'path: UTF-8 source file, at most 16384 bytes; body must return a JSON string. ' 'Example body: return PDVector(thePlayer.GetWorldPosition());', READY + ' Owned evaluation script hash unchanged.', 'Writes/compiles the evaluation script; arbitrary code runs with engine privileges. On compile failure restores/recompiles prior source.', 'compile, revision, result, artifacts; failures retain executed/rollback evidence. Runtime effects have no automatic rollback.', ['evaluate /absolute/path/experiment.ws'])command('recompile', 'Invoke the actual game script compiler.', 'No arguments.', 'Exactly one game process with its actual window and debugger enabled.', 'Reloads installed scripts. Does not deploy source or automatically roll it back.', 'ok, errors (original compiler packets), warnings count, artifacts. Type/field layout changes require a cold restart.', ['recompile'])command('stage', 'Deploy and compile the automation bridge with source rollback.', 'No arguments.', 'Live responsive game and hash-owned bridge deployment receipt.', 'Updates ONLY bridge.ws.in, not arbitrary combat mods. On failure restores/recompiles previous bridge source.', 'build on success; compiler errors/rollback on failure; staging_artifacts always on completed attempt. Layout changes require restart.', ['stage'])command('deploy', 'Deploy the bridge while the game is closed.', 'No arguments.', CLOSED, 'Backs up and replaces ONLY the owned bridge script; updates its receipt.', 'build, compilation=unverified. Launch and verify actual compiler success next.', ['deploy'])command('install-native', 'Update already-owned native adapter binaries.', 'No arguments.', CLOSED + ' Build first with python3 native/build.py; local toolchain/dependencies required.', 'Replaces PathInput.asi and WitcherNativeBridge.asi; retains prior files and receipt. Not a first-time installer.', 'native_installed=true and rollback_artifacts.', ['install-native'])command('install-ui', 'Update already-owned UI override bundle and index.', 'No arguments.', CLOSED + ' Build first with research/python-env/bin/python -m witcher_devkit.ui_assets; local assets/dependencies required.', 'Replaces two owned UI files; retains prior files and receipt. Not a first-time installer.', 'ui_installed=true and rollback_artifacts.', ['install-ui'])command('uninstall', 'Remove hash-owned automation files, retaining a backup.', 'No arguments.', CLOSED, 'Removes devkit scripts/UI and three native loader/adapter files; moves receipts to artifacts. Refuses modified/untracked files.', 'removed and artifacts. Does not remove the combat mod or restore game saves.', ['uninstall'])for name, summary, arguments, effects, returns, examples in [ ('ui-tree', 'Read the live instrumented display tree.', '', 'Refreshes UI snapshot revision; older raw references become stale.', 'revision, nodes (up to 4096), truncated, session. Inspect labels/paths before choosing a selector.', ['ui-tree', 'ui-tree --root hud:Minimap2Module']), ('ui-find', 'Wait for exactly one UI node matching an exact selector.', 'field: label|text|name|class|path. value: exact case-sensitive string. --timeout: finite seconds >0 and <=60 (default 10).', 'Observes UI; refuses duplicate matches and truncated trees. No input.', 'revision and node. Match alone does not guarantee the node is actionable.', ['ui-find label Settings']), ('ui-dispatch', 'Dispatch once to a stable, actionable, uniquely selected UI node.', 'field/value and --timeout as ui-find. --expect-label TEXT: wait for a unique resulting label.', 'Checks attachment, content, visibility, enabled state and stable bounds, then sends synthetic Flash mouse events; no pixel-occlusion test.', 'reference, delivery, transport, effect_verified. Without --expect-label effect_verified=false. A failed postcondition retains delivery evidence; never retry blindly.', ['ui-dispatch label Settings --expect-label Sound']),]: command(name, summary, arguments + ' --root: default deepest active menu, or popup:NAME / hud:NAME (1..80 alphanumeric/underscore characters).', UI, effects, returns, examples)
command('call', 'Use the low-level bridge protocol; prefer completion-aware commands above.', 'command followed by an optional quoted payload. See call <command> --help.', LIVE, 'Depends on subcommand; raw actions usually only acknowledge scheduling.', 'Bridge envelope: protocol, build, session, request, generation, engine_time, ok, data. Errors retain the original envelope as evidence.', ['call snapshot', "call spawn 'nekker|1|6|4'"])
def raw(name, summary, arguments, effects, returns, example, requires=READY): command('call ' + name, summary, arguments, requires, effects, returns, [example])
raw('hello', 'Initialize/inspect the bridge session.', 'No arguments.', 'Acquires no-save protection; normally called by attach.', 'Snapshot envelope.', 'call hello', LIVE)raw('snapshot', 'Observe readiness, player, nearby actors, ticks and UI.', 'No arguments.', 'Observation only; updates actor registry.', 'data.ready, ticks, remaining_ticks, input_ticks, ui, world. Up to 48 nearby actors within 40m; unavailable stats may be -1.', 'call snapshot', LIVE)raw('actor', 'Inspect a generation-local actor reference.', 'generation|id: decimal generation up to 10 digits, ID up to 3 digits; use values returned by snapshots.', 'Observation only.', 'Actor data or stale/unavailable error. Actor registry is bounded to 512 entries.', "call actor '10|1'")raw('events', 'Read the rolling native attempt/damage-action buffer without paging.', 'No arguments.', 'Observation only; does not consume events.', 'Most recent 128 version-2 events. Rich profiles may exceed the response-size limit; prefer top-level events for bounded paging, cursors and explicit gap reporting.', 'call events')raw('saves', 'List save filenames accepted by load.', 'No arguments.', 'Observation only.', 'Exact save names; choose deliberately instead of using continue.', 'call saves', LIVE)raw('pause', 'Pause simulation using the devkit pause reason.', 'No arguments.', 'Adds only the devkit pause reason.', 'Player snapshot. Needed before bounded input/advance.', 'call pause')raw('resume', 'Remove the devkit simulation pause.', 'No arguments.', 'World resumes unless another pause reason remains.', 'Player snapshot. Usually needed for menu input.', 'call resume')raw('timescale', 'Adjust the devkit time scale.', 'factor: finite number 0.05..2; 1 removes the devkit multiplier.', 'Changes simulation time scale.', 'Acknowledgement; observe simulation separately.', 'call timescale 0.25')raw('teleport', 'Move Geralt to a safe navigation point near coordinates.', 'x|y|z: three finite numbers, each absolute value <=100000, in the current region.', 'Moves player; rejects cutscenes and absent safe navigation within 3m.', 'Acknowledgement; inspect snapshot position.', "call teleport '100|200|10'")raw('spawn', 'Create tracked nonpersistent test actors.', 'template|count|level|distance: template alias or forward-slash depot path; count integer 1..8; level integer 1..100; distance finite 3..30 metres.', 'Up to 24 owned actors total. Initialization is asynchronous. Navigation/creation failure can leave partial spawns.', 'Spawn acknowledgement; poll snapshot to verify level/location. Use cleanup or exact-save load to reset.', "call spawn 'nekker|1|6|4'")raw('cleanup', 'Remove owned test actors and temporary control state.', 'No arguments.', 'Destroys owned spawns, cancels input/ticks, clears devkit pause/time scale. Does not release the no-save lock.', 'Acknowledgement. Does NOT reset health, ambient actors or quest state; use load for world reset.', 'call cleanup')raw('load', 'Request a save load without waiting for readiness.', 'Exact filename from call saves.', 'Loads once and discards unsaved state.', 'load_requested only. Prefer top-level load, which waits for a new generation.', 'call load manualsave_52586_7ea4bc00_5c355be', LIVE)raw('exit', 'Request engine exit without auditing saves.', 'No arguments.', 'Exits game.', 'Acknowledgement only. Prefer stop for termination wait and save audit.', 'call exit', LIVE)raw('ui', 'Observe menu stack and popups.', 'No arguments.', 'Observation only.', 'Menu/popup state; usable during startup.', 'call ui', LIVE)raw('ui_items', 'List main/pause menu item IDs and labels.', 'No arguments.', 'Observation only.', 'Items from deepest IngameMenu; use ui-tree for generic menus.', 'call ui_items', UI)raw('ui_click', 'Use an implemented main/pause menu action.', 'resume|continue (only these two actions are implemented).', 'Invokes the normal menu handler. continue loads the LAST save, which may not be your intended fixture.', 'Acknowledgement, not completed effect; prefer top-level load with an exact save.', 'call ui_click resume', UI)raw('menu', 'Request a menu transition using game handlers.', 'inventory|character|map|journal|pausemenu|close.', 'Opens/closes UI; transition is asynchronous.', 'Acknowledgement; inspect call ui / ui-tree for resulting state.', 'call menu inventory')raw('skip_intro', 'Request skipping the current startup/recap movie.', 'No arguments.', 'Skips only a supported active intro menu.', 'Acknowledgement; no_intro_menu means wrong state. Observe call ui before/after.', 'call skip_intro', LIVE)raw('input', 'Schedule bounded input without waiting for completion.', CATALOG['input']['arguments'], 'Schedules input and simulation ticks.', 'input_scheduled only; prefer top-level input for completion verification.', "call input '0|0|0|0|AttackLight|60'", PAUSED)raw('advance', 'Schedule simulation ticks without waiting for completion.', 'ticks: integer 1..600.', 'Schedules ticks and re-pause.', 'advance_requested only; prefer top-level advance.', 'call advance 30', PAUSED)raw('pad', 'Set native controller state with an automatic expiry lease.', 'lx|ly|rx|ry|buttons|left_trigger|right_trigger|lease_ms: axes [-1,1]; buttons unsigned 0..65535 excluding bits 0x0c00; ' 'triggers integers 0..255; lease integer 1..10000 ms. XInput masks: A=4096 B=8192 X=16384 Y=32768, ' 'd-pad U/D/L/R=1/2/4/8, start=16 back=32, thumb L/R=64/128, shoulders L/R=256/512.', 'Delivers raw gamepad input until expiry/release; does not control pause or verify effects.', 'pad_state_set. Button meaning depends on game bindings; example presses B.', "call pad '0|0|0|0|8192|0|0|300'", LIVE)raw('release', 'Cancel input and pending bounded actions.', 'No arguments.', 'Neutralizes virtual input; cancels pending ticks. Does NOT pause simulation.', 'input_released.', 'call release', LIVE)raw('evaluate', 'Invoke the currently compiled evaluation body once.', 'No arguments.', 'Runs arbitrary trusted code already staged by evaluate.', 'The body\'s JSON value. Prefer top-level evaluate to compile a specific file first.', 'call evaluate', LIVE)raw('ui_scan', 'Start a raw UI snapshot and invalidate previous references.', 'Optional root: popup:NAME or hud:NAME; omitted means deepest active menu.', 'Refreshes devkit UI cache, not game UI.', 'revision, count, truncated. Prefer ui-tree for automatic pagination.', 'call ui_scan', UI)raw('ui_rows', 'Read a page from the current raw UI snapshot.', 'revision|start|count: revision integer 1..2147483646, start 0..4095, count 1..32.', 'Observation only.', 'Cached rows; stale revision fails.', "call ui_rows '1|0|32'", UI)for name in ('ui_node', 'ui_dispatch'): raw(name, 'Validate a raw UI node.' if name == 'ui_node' else 'Dispatch synthetic mouse events to a raw UI node once.', 'revision|id: revision integer 1..2147483646, id 0..4095, from the most recent scan.', 'Observation only.' if name == 'ui_node' else 'Sends mouseDown/click; prefer ui-dispatch for stability checks and a postcondition.', 'Validated node / dispatch evidence; stale, detached, changed or unavailable targets fail. No effect guarantee.', "call " + name + " '1|3'", UI)
GUIDES = { 'quickstart': '''Same-machine agent handoff1. Run doctor, then verify. A source-only clone lacks receipts, binaries and assets; it is not a first-time installer. Use the configured original workspace.2. Run launch; it automatically enables 1080p windowed/background/Desktop 2 mode. Wait for the actual "The Witcher 3" window, not the splash or port. Run attach once, then call ui. Only call skip_intro if that UI state needs it.3. Run call saves. Choose the intended exact filename, then load <filename>. The known post-Keira fixture is manualsave_52586_7ea4bc00_5c355be; verify it exists.4. Run call pause, then call snapshot. Inspect readiness, generation, equipment, health, position and nearby actors before choosing an encounter.5. Use input / advance for bounded actions; compare events and call snapshot. input_delivered is not proof of damage. Read guide combat for the experiment loop.6. Reload the same save to reset; references from the old generation are invalid.7. Run stop and check save_files_unchanged and game_settings. It automatically restores personal display/focus/workspace settings after exit. Steam Play then uses personal settings; another lab launch automatically enables lab mode again. After a crash or exit outside stop, close the game and run reset-game-settings (plus perf-restore if using performance capture).
Only one agent should own the live session. A command lock serializes individualcalls; the experiment runner holds it throughout its whole plan. Terminal access to this workspace is sufficient;there is no separately registered MCP server or required SDK. Help works offline.''', 'combat': '''Combat iteration contractAgree the combat model before implementing it. The installed baseline includesthe earlier modPathCombat prototype and Skills refixed; account for both whenmeasuring. Preserve modPathDevkit and its native/UI dependencies.
Record a baseline: exact save, difficulty, upscaling, equipment, skills, health,enemy type/level, positions, generation and event sequence. Position a suitablearena with top-level teleport, then spawn nekker --level 6. These helpers verifyarrival and initialized level before measuring. Inspect profiles and weapon state.SilverSword input toggles draw/holster; native auto-draw may already have drawn it.input '0|0|0|0|AttackLight|60' delivers an attack; compare health and damage eventsto establish a hit. Events retain only 128 records; collect frequently and detectsequence gaps with top-level events. Native DoAttack attempts include missed swings;separate damage-action contacts include primary and secondary/DoT contexts.Use guide observations for stage semantics and guide experiments for plans.
Edit -> deploy owned combat files -> recompile -> inspect compiler result ->reload exact save -> recreate fixture -> measure -> compare -> retain or rollback.The combat agent must provide a scoped deployment/rollback path for its own mod.stage updates ONLY the automation bridge; it is not a generic combat deployer.Use a scoped installer owned by the consuming project; preserve existing mods anddevkit files. Keep backups and ownership hashes for project files. Recompile doesnot automatically roll them back.
evaluate file.ws can compile a trusted function body returning JSON and executeit once. Example body: return PDVector(thePlayer.GetWorldPosition());Body-only changes can hot reload; fields/type layout changes need a cold restart.Compile rollback restores source, not arbitrary runtime effects. Save reloadresets world state, not necessarily all process-global/static state; use a coldrestart when comparing changes to those. No deterministic render replay claim.''', 'ui': '''UI observation and actioncall resume allows normal menu input; call menu inventory requests a transition.call ui reports menu state; ui-tree returns the actual instrumented display tree.ui-find label Settings waits for a unique exact match (not a fuzzy text search).ui-dispatch label Settings --expect-label Sound verifies an observed postcondition.Labels depend on the current screen and language: inspect first, do not guess.Use --root popup:NAME or hud:NAME for those roots; default is deepest active menu.
Strict selectors refuse duplicates and partial trees. Dispatch revalidates objectidentity/content, ancestor visibility/enabled state and bounds stability. Eventsare synthetic Flash mouse events, not physical mouse clicks or occlusion tests.Raw call pad can follow the game's normal controller path. Leases auto-expire.Failed postconditions retain delivery evidence; inspect state before anotheraction. Screenshots are desktop captures, not synchronized rendered frames.108 movies were instrumented, but every screen has not been tested.''', 'recovery': '''Diagnose without replayingOperational failures print one JSON object on stdout and a human diagnostic onstderr. Read diagnostic.code, context, recovery and original result evidence.Use explain CODE for offline remediation, and help <command> for its contract.
For lost acknowledgement/timeout: status, call snapshot, events, and call uiwhen relevant. A timeout can occur after the action executed. Do not retry itblindly. Read-only observations remain allowed while a mutation is uncertain.recover clears only the local block after you have reconciled actual state.It never undoes/repeats an action, nor proves a failed compile rollback succeeded.If runtime code is uncertain, inspect retained source/compiler artifacts andrestore a known build, then cold restart and attach. Do not merely clear a flag.
For compiler failures: errors retain original compiler packets with file/lineinformation; artifacts point to complete logs. stage/evaluate restore the oldsource and attempt to compile it. Check rollback.ok / rollback_compiled explicitly.recompile alone never restores source. For save audit failure, inspect the hashdiff against artifacts/<session>/saves-before.json; no automatic restoration.
For stale actor/UI references, observe again and select using the new generationor revision. For missing receipts on a clone, use the original configured host;install-native/install-ui are updates, not bootstrap commands.''', 'protocol': '''CLI contract version 1Every operational command emits one JSON object on stdout: ok=true on success,ok=false on failure. Exit 0=success, 1=operation/setup failure, 2=usage error.Errors also print a readable diagnostic to stderr. Preserve stdout separately;do not merge streams when parsing JSON. Error strings are for humans; branch ondiagnostic.code. Original engine/compiler evidence remains available in result.
commands --json emits versioned command contracts and argument constraints;help <command> --json emits one contract. guide [topic] --json and explain CODE--json provide the same offline guidance in machine-readable form. --help isalways human text. Documentation never contacts the engine or takes its lock.Quote pipe-delimited payloads. Use -- before a positional payload beginning '-'.
Bridge calls carry protocol/build/session/request/generation identity. A completeacknowledgement does not necessarily mean a completed gameplay/UI effect. Prefertop-level load/input/advance and explicit UI postconditions for completion waits.Raw call commands expose scheduling-level results. Read each command's completioncontract before writing assertions. Extra result fields may be added; tolerate them.
State, receipts, traces and backups live in shared installation-scoped XDG state,not the executing worktree. doctor shows the selected paths. Source-only help isportable; guide setup documents discovery and closed-game migration.''',}
def render(spec): def paragraph(value): return textwrap.fill(value, width=88, initial_indent=' ', subsequent_indent=' ') return '\n\n'.join([spec['summary'], 'Arguments:\n' + paragraph(spec['arguments']), 'Requires:\n' + paragraph(spec['prerequisites']), 'Effects:\n' + paragraph(spec['effects']), 'Completion / output:\n' + paragraph(spec['completion']), 'Examples:\n' + '\n'.join(' ' + PREFIX + e for e in spec['examples']), 'Next: guide quickstart | guide recovery | explain CODE\n' 'Operational stdout is JSON; exit 0=success, 1=failure, 2=usage error.'])
command('profiles', 'Inspect Geralt and all owned test actors through native read-only queries.', 'No arguments.', READY, 'Observes profiles and settings; does not deploy an evaluation body.', 'generation, settings, profiles with health availability, defenses, power terms, effects, equipment, ability multiplicities.', ['profiles'])command('profile', 'Inspect one generation-local actor in detail.', 'reference: generation|id from snapshot.', READY, 'Observation only; stale generations fail.', 'Bridge profile with native unavailable values normalized and retained as native_value.', ["profile '10|0'"])command('ability', 'Inspect native ability existence and raw attribute terms.', 'name: native ability identifier (letters, numbers, underscore).', READY, 'Calls IsAbilityDefined and enumerates attributes; never applies an ability.', 'defined distinguishes missing from empty enumeration; min/max base/multiplicative/additive. Inheritance provenance is unavailable.', ['ability MonsterLevelBonusGroup'])command('templates', 'List resource paths, variants and live verification status for encounter templates.', 'No arguments.', 'None; offline catalog.', 'Reads the local catalog only.', 'templates with exact paths and verification status. E3 variants are explicitly distinguished from generic bandits.', ['templates'])command('spawn', 'Spawn once and verify initialized actor levels and profile stability.', 'template: alias or forward-slash depot path. --level required integer 1..100; --count 1..8 (default 1); --distance 3..30 (default 20); ' '--max-ticks 1..600 (default 180); --step 1..120 (default 15); --stable-samples 2..10 (default 3).', PAUSED, 'Spawns once, advances bounded ticks and observes. Never adjusts health/damage or silently substitutes an actual level.', 'requested, observed, history, elapsed_ticks, stability heuristic, experimental_validity and outcome. Mismatch fails; dependent actions must stop.', ['spawn nekker --level 6'])command('teleport', 'Teleport once and verify arrival before dependent setup.', 'position: x|y|z. --tolerance finite metres >0..5 (default .35); --max-ticks 1..600 (default 120); --step 1..120 (default 5).', PAUSED, 'Requests navigation-adjusted teleport once, advances and observes arrival. No mutation retries.', 'requested/navigation_target/observed, tolerance_m, elapsed_ticks, history and experimental_validity.', ["teleport '219|-473.9|5.015304'"])command('events', 'Collect bounded events with generation-aware sequence cursors and explicit gaps.', '--after nonnegative sequence (default 0); --generation required with nonzero cursor; --limit 1..128 (default 128).', READY, 'Read-only paged collection; does not drain events or advance time.', 'events, next_cursor, complete, gaps and more. Gap-free reads only cover the initial target sequence; persist the cursor for subsequent collections.', ['events'])command('evaluation-recover', 'Restore a scoped evaluation after interruption with ownership guards.', 'path: recovery.json emitted by evaluate --scoped.', 'Original local recovery receipt, unchanged owned target, responsive game for recompilation.', 'Restores original bytes, receipt hash and recompiles; refuses unrelated edits. Runtime effects are not undone.', 'source_restored, compile and receipt; failure retains explicit recovery status.', ['evaluation-recover /absolute/path/recovery.json'])for _name, _args, _example in [ ('profile', 'generation|id', "call profile '10|0'"), ('ability', 'Native ability identifier.', 'call ability MonsterLevelBonusGroup'), ('lab_state', 'No arguments.', 'call lab_state'), ('event_page', 'generation|after_sequence|limit (1..4)', "call event_page '10|0|4'"),]: raw(_name, 'Read native lab observations: ' + _name + '.', _args, 'Observation only.', 'Use the top-level profile/ability/profiles/events command for normalization and collection semantics.', _example)CATALOG['evaluate']['arguments'] += ' --scoped restores prior source after completion/failure and emits an interruption recovery receipt.'CATALOG['evaluate']['completion'] += ' Scoped mode includes restoration and compiler diagnostic_delta versus a baseline compile.'
raw('water_depth', 'Read raw water depth at the player position.', 'No arguments.', 'One read-only native query; no search, placement, advancement or suitability evaluation.', 'Position, raw_depth, tick and engine_time. Native sentinel 10000 is not a measured depth. Use debug inspect --view water for availability annotation. Build-specific live evidence is in water-query-validation.json; prior installation was restored.', 'call water_depth')raw('water_depth_actor', 'Read raw water depth at a generation-scoped actor position.', 'generation|actor_id.', 'One actor position and depth read; no movement or player fallback.', 'Identity, position, raw_depth and native clock. Partial build-specific evidence is in teleport-callback-validation.json. Use debug inspect --view water --reference for availability annotation.', "call water_depth_actor '10|0'")raw('teleport_observer_arm', 'Internal owner-only callback observer arm.', 'generation|cleanup_owned_actor_id|ticks (1..600).', 'Only the debugger owner process may arm. Use debug observe-teleport for a finite wall-bounded window; raw CLI calls are rejected.', 'Observer identity/status, or explicit ownership rejection. No movement or eligibility calls. See teleport-callback-validation.json for partial qualification and unexercised paths.', "call teleport_observer_arm '10|0|60'")raw('teleport_observer_stop', 'Internal owner-only callback observer stop.', 'generation|observer_id.', 'Only the debugger owner process may disarm; no gameplay mutation.', 'Inactive observer status/counters; stale observer identity rejects.', "call teleport_observer_stop '10|1'")raw('teleport_observer_status', 'Read callback observer status and retained drop counters.', 'No arguments.', 'Read-only; does not arm, step, pause or disarm.', 'Observer identity, active flag, deadline tick, records/dropped/overflow and termination reason. Use debug inspect --view teleport-observer while owned.', 'call teleport_observer_status')
raw('request_algorithm_arm', 'Internal owner-only finite request-algorithm guard.', 'generation|actor_ids_csv|ticks|wall_ms|mode|scores_csv; 1..8 distinct alive owned actors, exactly N score slots, ticks1..600, wall_ms1..60000, mode observe/score. Observe slots must all be zero; score slots .000001..1000000.', 'Owner only. Use debug request-algorithm. Natural original runs once; eligible positive-output replacement only. No source or count mutation.', 'Window identity/readback. Count mode and runtime node identity unavailable.', "call request_algorithm_arm '10|1,2|60|10000|observe|0,0'")raw('request_algorithm_stop', 'Internal owner-only request guard disarm.', 'generation|window_id.', 'Owner only. No reversal of past allocator decisions.', 'Inactive/armed=false matching window.', "call request_algorithm_stop '10|1'")raw('request_algorithm_status', 'Read request guard state and bounded-buffer counters.', 'No arguments.', 'Read-only. Active means effective lease; armed may remain true until the next guard tick or explicit stop.', 'Identity, actor IDs, mode, scores, expiry, records/dropped and explicit attribution limits.', 'call request_algorithm_status')raw('request_algorithm_page', 'Read retained natural algorithm records.', 'generation|window_id|start (0..128), <=4 records.', 'Read-only; bounded separate non-consuming buffer. Read stable pages after disarm.', 'Original/returned algorithm output and raw count fields, not effective quantity, allocator importance or admission.', "call request_algorithm_page '10|1|0'")
raw('crossbow_state', 'Inspect selected/equipped ranged items and current input/firing evidence.', 'No arguments.', 'Read-only loaded Witcher state. Does not equip, select, fire, query AI eligibility or invoke a projectile callback.', 'Selected/equipped items, raw guard/parry flags, bounded parry/crossbow lock sources, AltSign modifier, holster/combat-action state, compiled buffer enum symbols/values and engine input values. Snapshots are not per-edge execution traces or readiness guarantees. Stock ephemeral fire fact and one tagged bolt are initiation hints, not final target/impact. No buffer processing, lock/flag writes or eligibility calls. Use debug inspect --view crossbow while owned.', 'call crossbow_state')raw('quickslot', 'Internal owner-only reversible selection of existing equipment.', 'generation|crossbow/clear/restore|selection_id; crossbow/clear require0, restore the returned positive receipt.', 'Alive paused/no-save Witcher. Selection only; never adds/equips/removes inventory. Prior item must remain on its captured equipment slot or restore rejects. One active receipt.', 'Generation-scoped receipt/state or native rejection. Reload/closure discards unsaved world; restoration of selection does not undo firing/ammo/queued actions.', "call quickslot '10|crossbow|0'")
raw('lab_config', 'Set declared runtime controls for a protected comparison.', 'difficulty|upscaling|prototype_disabled: difficulty 1..4, booleans 0 or 1.', 'Changes runtime difficulty through the native change handler, upscaling and existing prototype toggle. Does not write settings files. Difficulty may change lowest-used history in unsaved world state.', 'Acknowledgement only; inspect lab_state. Reload exact fixture and reapply declared controls before each trial; reload again when restoring historical state.', "call lab_config '4|1|0'", PAUSED)command('experiment', 'Run sequential exact-save trials and retain checkable evidence.', 'plan: JSON file. See guide experiments for strict schema. --keep-open leaves the game paused; default exits and audits saves.', PAUSED, 'Resets fixture per trial, verifies setup, applies declared runtime controls, executes bounded actions and collects evidence. Owns the CLI lock for the whole run.', 'Command success is separate from all_trials_passed and per-trial experimental_validity/outcome. Report version 2 scopes assertions to encounter participants, retains matched event IDs and detects external contacts involving either side. result and manifest paths; no model-validation claim.', ['experiment /absolute/path/plan.json'])command('verify-evidence', 'Verify exported experiment files against their content manifest.', 'manifest: manifest.json from experiment. --current-sources also detects drift in original source/mod files.', 'None; offline files.', 'Reads files only.', 'checked and failures; does not establish experiment semantics.', ['verify-evidence /absolute/path/manifest.json'])GUIDES['experiments'] = '''Sequential experiment plansUse experiment plan.json after launch/attach/load/call pause. The runner holds the commandlock through all trials, resets the exact save each time, verifies actual levelsand arrival, records profiles and bounded events, then restores runtime controlsand by default exits through stop with a save-file audit.
Minimal plan (exploration, no assertion or combat-model claim):{"fixture":"manualsave_52586_7ea4bc00_5c355be","repeats":1,"trials":[ {"name":"nekker6","arena":"219|-473.9|5.015304", "enemy":{"template":"nekker","level":6,"count":1,"distance":4}, "actions":[{"advance":60},{"require_weapon":"silver"}, {"input":"0|0|0|0|AttackLight|60"}]}]}
Plan requires fixture (exact save filename) and trials (1..32 objects). Optional:repeats integer 1..10 (default 1), description metadata, stop_after boolean.The CLI controls stopping through --keep-open and overrides plan.stop_after;omitting --keep-open always stops and audits saves.Each trial requires a unique nonempty name, arena as 'x|y|z', enemy, and actions.enemy requires template (catalog alias or depot path) and level integer 1..100;optional count integer 1..8 (default 1), distance number 3..30 (default 4).Use templates to inspect suitability before choosing an enemy.Unknown keys are rejected in plan, trial, enemy, settings, assertions and actions.Each trial may declare settings (difficulty integer 1..4, upscaling boolean,prototype_disabled boolean), policy text, declared_variable text and assertions.Assertions: minimum_contacts and minimum_incoming_contacts are nonnegative integercounts of outgoing and owned-enemy incoming damage actions, respectively;health_channel is vitality/essence; no_contacts is boolean. required_flags is a listdrawn from parried, countered, dodged, critical, armor_zero, parry_stagger,ends_quen_flag. No assertions meansinconclusive exploration, not a passed model test. Misses are attempts without anobserved contact, not zero-damage hits. Trace gaps make the trial inconclusive.required_flags uses flag_scope: encounter (default, player <-> owned enemy),incoming (owned enemy -> player), or outgoing (player -> owned enemy). To testGeralt dodging, use {"required_flags":["dodged"],"flag_scope":"incoming"}.flag_match: independent (default) allows each requested flag on a separate contact;same_contact requires every requested flag on one contact. Scope/match optionsrequire a nonempty required_flags list. Results retain scope and matched_eventswith generation, sequence, contact/attempt IDs and actors, within the report session.no_contacts checks only player <-> owned enemy contacts; unrelated world fightscannot satisfy flags, add to minimum counts, or fail no_contacts. Count/channelassertions keep their own documented direction regardless of flag_scope.External contacts involving either side separately invalidate the trial, even ifassertions pass. summary.environmental_interference retains sequence IDs;interference_details records actors, causers, roles and direction. Unknown attackeror victim endpoints involving a participant are conservative interference. A knownexternal actor causer is also interference; an unavailable causer alone is notproof of an outsider when attacker/victim are known participants. Contact telemetrycannot detect every environmental or AI influence. Trial validity is not isolation proof.Actions allow input payloads, advance integer 1..120, require_weapon steel/silver, wait_input_ready integer 1..600, or wait_counter_window integer 1..600.Each action object contains exactly one key. For input payload syntax, button namesand bounds, use help input --json. An empty actions list is allowed for setup-only trials.A require_weapon step is a read-only precondition, never a draw toggle. Let nativeauto-draw settle first; sending a sword button while already drawn can sheath it.wait_input_ready advances in at most five-tick blocks until native input is allowed;all waiting ticks/events are retained and the declared budget is enforced.wait_counter_window observes a new native counter-window timestamp on the firstowned enemy, in five-tick blocks. Follow it with one Guard input; repeated guardpresses can fail the game's counter-spam check. No native timestamp is changed.Collect between bounded steps; minimum_incoming_contacts asserts owned incoming hits.Limits: 32 scenarios, 10 repeats, 100 steps per scenario. Use a deliberate shortresponse policy before longer group trials. Any level mismatch stops that trial.
Default experiment completion exits the game and compares save hashes. --keep-openretains the paused session for additional inspection. Exact-save reload restoresworld state; runtime settings are verified after applying them. RNG and AI are notdeterministic. Inspect before profiles and uncontrolled factors before comparisons.Artifacts include full individual trials, shareable condensed.json, original input copies, effective modfiles, command/state/event records and a manifest. verify-evidence checks content;--current-sources separately checks drift. Never commit extracted game/mod assets.'''
command('combat-state', 'Read Geralt and selected actors together without advancing simulation.', 'Zero or more generation|actor_id references; default Geralt plus owned spawns. At most 24 selected plus Geralt.', PAUSED, 'One script invocation; reads native state and registers returned actor/entity references. No AI decision, ticket acquisition, chance roll or damage calculation.', 'Tick, engine time, input availability, history, adrenaline, per-actor storage and combat-component availability, attacker registrations and five ticket-owner sets. Ownership is not simultaneous attack count; missing getters include reasons.', ['combat-state', "combat-state '10|0' '10|2'"])command('encounters', 'Discover loaded nests/encounters and page stable inspection references.', '--radius 1..1000 metres (default 100), --tag optional native tag, --limit 1..32 (default 16). ' 'Subsequent pages use --generation G --revision R --start NEXT from the first response.', READY, 'Read-only world queries update a separate inspection registry/cache. Never adds world entities to cleanup-owned spawns. A fresh scan invalidates old page revisions, not entity refs.', 'Discovery scope and possible_truncation, page entities, next/more. Nearby uses two capped native spatial queries; tag queries ignore radius and native enumeration is uncapped, returned results capped at 256. Empty is not proof of no authored encounter.', ['encounters --radius 100 --limit 8', 'encounters --generation 10 --revision 1 --start 8 --limit 8'])command('encounter', 'Inspect a generation-local world entity, nest link or encounter.', 'reference: generation|entity_id from encounters, profile.parent_encounter, or a resolved nest link. Entity and actor IDs are separate namespaces.', READY, 'Observes links and native enabled/area state. No phase change, discovery marking, fact mutation or destruction.', 'Runtime provenance and explicit unavailable reasons for current phase, authored spawn-tree traversal and active initializer bindings. Configured nest phase names are not the active phase.', ["encounter '10|0'"])command('sample', 'Record combat state inside script ticks during one bounded action.', '--ticks 1..600 (default 60); --every 2..120 (default 2); optional --input full payload with matching ticks (input max 120). ' '--actor generation|id repeatable, default Geralt plus owned spawns.', PAUSED, 'Arms a separate bounded observer buffer, delivers exactly one input/advance under the CLI owner lock, then collects after re-pause. Advances simulation; no compilation. Up to 301 samples including the paused baseline.', 'Normal bounded action result plus sampling: actual_ticks/spacing, missing_ticks/pages, termination, complete and samples. Complete covers observed ticks, not successful completion of the requested action. Early death and action rejection remain explicit. No deterministic render-frame promise.', ['sample --ticks 60 --every 2', "sample --ticks 60 --input '0|0|0|0|Guard|60'"])for _name, _args, _example in [ ('combat_state', 'generation|owned or generation|comma-separated-actor-ids', "call combat_state '10|owned'"), ('encounter_scan', 'radius|tag (empty tag uses nearby spatial discovery)', "call encounter_scan '100|'"), ('encounter_page', 'generation|revision|start|limit', "call encounter_page '10|1|0|16'"), ('encounter', 'generation|entity_id, separate inspection registry', "call encounter '10|0'"), ('sample_arm', 'generation|selection|every_ticks|budget_ticks', "call sample_arm '10|owned|2|60'"), ('sample_stop', 'generation|revision', "call sample_stop '10|1'"), ('sample_page', 'generation|revision|index, one stored sample per call', "call sample_page '10|1|0'"),]: raw(_name, 'Native encounter observation protocol: ' + _name + '.', _args, 'Only observation bookkeeping changes; sample_arm requires pause and reserves a sampler until stopped or budget expires. Never advances time by itself.', 'Acknowledgement/data only. Prefer combat-state, encounters, encounter or sample for workflow guards and collection. Stale generations/revisions fail.', _example)GUIDES['encounters'] = '''Pause, then combat-state to observe Geralt and owned actors in one script invocation.Pass generation|actor_id to include registered outsiders; Geralt is always included.CombatData storage and CCombatDataComponent each report availability. Tickets listnative TICKET_Melee/Charge/Range/Special/Approach owners, including outsiders.Registered attackers, ticket owners and currently attacking actors are differentobservations. Empty owner arrays are observed emptiness, not proof a pool exists.No chance evaluator, ticket acquisition or importance calculation is called.
encounters --radius 100 returns loaded nests/encounters, not the whole world.Use --tag when an authored tag is known (radius is ignored). Reuse generation,revision and next as --start for stable cached pagination. Fresh scans invalidatepage revisions. Entity references persist only in this loaded world generation.encounter generation|entity_id follows nest linkage and inspects enabled/area state.profile generation|actor_id exposes NPC GetParentEncounter separately from proximity.Inspection handles never enter cleanup's owned-spawn array. Missing parent is asupported null result; missing traversal/getter is available=false with a reason.
profile includes cached GetThreatLevel separately from the threat_level attribute,raw and effective dodge/counter parameters, and unknown definition/consumer fields.Zero does not prove an absent definition or an active consumer. Missing abilityinitialization is unavailable. No health/level compensation is applied by spawn.
sample --ticks 120 --every 2 captures baseline plus every second OnTick after theoriginal tick callback. Optional --input is delivered once, uninterrupted. Samplingis not frame synchronization and adds observation overhead. Read actual spacing,missing samples, termination and the enclosing action's completion/death result.The buffer is separate from the existing event ring; collect events with cursorsas usual. Raw sample_arm/page/stop supports integrations; a new arm invalidates oldsample revisions. No compiler call is needed per sample. Runner trials may opt inwith "sampling":{"every_ticks":2}; each advancing step/wait block then retainsits samples in the response, under the runner's existing owner lock.
Only the concrete monster-nest monitor initializer's spawned/lost/killed callbacksare observed, after exactly one original call. Events retain actual entry fieldsand parent encounter; loose spawns and other initializer subclasses have no suchprovenance. Configured default/boss phase names are not active phases. Native actorAPIs do not expose active task enumeration or task-protected AI parameters. Authoredspawn-tree/initializer inheritance requires offline assets. No ticket override orAI treatment is part of this delivery; E00–E05 research trials remain unexecuted.'''
command('asset-inspect', 'Inspect serialized properties and bindings in one installed resource.', 'path: exact depot path; forward or backward slashes. Follow returned imports explicitly.', 'Installed bundles and local audited Conjunction checkout under research/repos/Conjunction. No game session needed.', 'Reads bounded resource bytes only. No extraction to game directories, asset mutation, inheritance merge or engine invocation.', 'All candidate versions with hashes/bundles, embedded CR2W parts, exports/imports, typed properties and undecoded reasons/raw prefixes. Active bundle/task availability stays false. Limits: 32 candidates, 32 MiB each, 32 parts, 2048 exports/part.', ['asset-inspect characters/npc_entities/monsters/ghoul_lvl2.w2ent'])GUIDES['assets'] = '''asset-inspect characters/npc_entities/monsters/ghoul_lvl2.w2ent reads installedserialized template properties. It uses the local Conjunction bundle/CR2W readeralready audited for this remaster, under research/repos/Conjunction.Its source and extracted game resources are not redistributed; honor the localcheckout's license. Codec 4/5 uses the bounded built-in raw LZ4 block decoder;the main Python needs no optional lz4 extension or research-env PYTHONPATH.Run that environment's Python with cli.py if the system interpreter lacks it.The JSON records reader hashes and every matching bundle/version. Different bytesstay separate because filesystem enumeration does not establish engine priority.
Follow CEntityTemplate includes and behavior/AI/spawn-tree imports explicitly withanother asset-inspect. Object properties carry serialized provenance; absentproperties can inherit or use script defaults. Unsupported types retain a reasonand bounded raw prefix, not an invented value. Handles resolve import/export IDswithin their containing CR2W part; nested parts have separate namespaces.This gives an offline access path for templates, initializer bindings, quantities,intervals and AI overrides where serialized, without a broad editor. It does notprove which inherited task is executing. GetAIParametersByClassName is protectedto a behavior task; no audited actor-side active-task enumerator was found.Use runtime profile, combat-state and actual monitor callbacks to corroborateserialized candidates. Never infer an NPC's spawn entry from mere proximity.'''
# Structured constraints are the same definitions used by runtime validation.# They describe the CLI payload, not a JSON input API.for _name, _spec in CATALOG.items(): _raw = _name[5:] if _name.startswith('call ') else _name _spec['parameters'] = SPECS.get(_raw, []) if _name.startswith('call ') or _name in ('input', 'advance', 'load') else [] _spec['payload_separator'] = '|' if len(_spec['parameters']) > 1 else None _spec['options'] = [] if _name.startswith('ui-'): _spec['options'] = [dict(ROOT, name='--root', default='')] if _name != 'ui-tree': _spec['parameters'] = [choice('field', 'label text name class path'), dict(name='value', type='string', match='exact')] _spec['options'].append(dict(number('--timeout', 0, 60), exclusive_minimum=True, default=10)) if _name == 'ui-dispatch': _spec['options'].append(dict(name='--expect-label', type='string', optional=True)) if _name == 'evaluate': _spec['parameters'] = [dict(name='path', type='file', encoding='UTF-8', max_bytes=16384)]
# Top-level completion-aware commands use separate CLI flags, not raw payloads.CATALOG['profile']['parameters']=[dict(name='reference',type='string',format='generation|id')]CATALOG['combat-state']['parameters']=[dict(name='references',type='array',max_items=24,items=dict(type='string',format='generation|actor_id'),default='Geralt plus owned')]CATALOG['encounter']['parameters']=[dict(name='reference',type='string',format='generation|entity_id')]CATALOG['asset-inspect']['parameters']=[dict(name='path',type='string',format='relative depot path',max_length=512)]CATALOG['encounters']['options']=[dict(SPECS['encounter_scan'][0],name='--radius',default=100),dict(SPECS['encounter_scan'][1],name='--tag',default=''),dict(SPECS['encounter_page'][3],name='--limit',default=16),dict(SPECS['encounter_page'][0],name='--generation',optional=True),dict(SPECS['encounter_page'][1],name='--revision',optional=True),dict(SPECS['encounter_page'][2],name='--start',default=0)]CATALOG['sample']['options']=[dict(SPECS['sample_arm'][3],name='--ticks',default=60),dict(SPECS['sample_arm'][2],name='--every',default=2),dict(name='--input',type='string',format='input payload; ticks must equal --ticks',optional=True),dict(name='--actor',type='string',format='generation|actor_id',repeatable=True,max_items=24)]CATALOG['ability']['parameters']=SPECS['ability']CATALOG['spawn']['parameters']=[SPECS['spawn'][0]]CATALOG['spawn']['options']=[dict(SPECS['spawn'][2],name='--level',required=True),dict(SPECS['spawn'][1],name='--count',default=1),dict(SPECS['spawn'][3],name='--distance',default=20),number('--max-ticks',1,600,True,default=180),number('--step',1,120,True,default=15),number('--stable-samples',2,10,True,default=3)]CATALOG['teleport']['parameters']=[dict(name='position',type='string',format='x|y|z')]CATALOG['teleport']['options']=[number('--tolerance',0,5,exclusive_minimum=True,default=.35),number('--max-ticks',1,600,True,default=120),number('--step',1,120,True,default=5)]CATALOG['events']['options']=[number('--after',0,2147483646,True,default=0),number('--generation',0,2147483646,True,required_when='--after is nonzero'),number('--limit',1,128,True,default=128)]CATALOG['evaluate']['options']=[dict(name='--scoped',type='boolean',default=False)]for _command,_file in [('evaluation-recover','path'),('experiment','plan'),('verify-evidence','manifest')]: CATALOG[_command]['parameters']=[dict(name=_file,type='file',encoding='UTF-8',format='JSON')]CATALOG['experiment']['options']=[dict(name='--keep-open',type='boolean',default=False)]CATALOG['verify-evidence']['options']=[dict(name='--current-sources',type='boolean',default=False)]
GUIDES['observations'] = """Native observations, version 2profiles / profile generation|id expose native actor and pool values, typedresistances, raw power terms, held equipment/oils, effects and ability counts.Pool available=false means normalized null plus retained native sentinel values.Uninitialized combat attributes are null with native_* fields retained. The nestedactor remains the raw snapshot vocabulary. Group classification is not group size.Ability counts including inventory overlap the actor-only list; never add lists.ability NAME distinguishes IsAbilityDefined from enumeration completion. Emptyattributes alone do not prove no effect. Inheritance/source provenance is unknown.
Events: DoAttack.entry creates a melee attempt; each ProcessAction produces aseparate contact/damage-action record. Several contacts can share an attempt.Delayed/projectile/DoT parentage may be unknown (attempt_id=0). request_context isthe last bridge request; it is not a substitute for native attempt correlation.ProcessAction entry captures actual raw components and victim pre-profile.CalculateDamage captures actual passed raw/power terms, original resistance outputsand original return values. ProcessAction exit captures all processed channels,health/max/alive changes and independent native flags. Wrappers run original codeonce and never reroll probability or damage. Calculation rows cap at 32; treatcalculation_capacity_reached conservatively as possible truncation.
Use the victim active health channel. Do not add vitality and essence into one wound.Parried/countered are attack fields; dodge/crit/parry-stagger/armor-zero remain nativeflags. Quen entry/exit objects expose active state and shield pools plus EndsQuen.These do not isolate every absorption contribution. Native reaction requests andsampled states/input availability are observations, not proof of every interruption.armor_zero can coexist with nonzero active-channel damage: one component may bereduced to zero while another still damages health. It does not mean whole-hit immunity.Unknown/nonactor causers and opaque inventory instance IDs remain unavailable.
Collect events with generation and sequence cursors. complete=false or gaps meansan incomplete trace; the ring retains 128 events. Attempt counts cover the wrappedmelee path, not every ranged/sign attempt. Contacts include secondary DoT actions;filter native context before computing sword-hit statistics. Damage-action contactdoes not by itself approve a model's qualifying-hit definition.
experiment records individual repeats, profiles, geometry, settings, commands,assertions, integrity manifests and shareable condensed.json. RNG/AI/load timingremain uncontrolled. A rolling 30-tick distinct-attacker count is an overlap proxy,not continuous animation overlap or an approved swarm balance target. See guideexperiments for setup and plans; verify-evidence checks files, not model semantics."""
# Performance captures are deliberately separate from script-tick sampling.command('perf-status', 'Discover qualified host presentation-call timing capability.', 'No arguments.', 'Local provider build; game may be closed.', 'Read process maps/environment and bounded shared header.', 'available/provider/process/clock/semantics or explicit reason. Missing provider is not zero cost.', ['perf-status'])command('perf-launch', 'Enable lab mode and launch with presentation telemetry and rollback journal.', '--proton PATH', CLOSED + ' Built presentation layer; active Steam Proton directory; same KDE/workspace/compiler requirements as launch.', 'Temporarily extends Proton user_settings.py for app 292030 only, then uses the same automatic 1080p windowed/background/Desktop 2 setup as launch. stop restores personal settings and capture configuration after exit.', 'Launch acknowledgement, lab_mode setup receipts and cleanup journal; attach then perf-status verifies real process. perf-restore restores Proton bytes only. After a crash or exit outside stop, close the game, run reset-game-settings and perf-restore.', ['perf-launch --proton "/path/to/Proton - Experimental"'])command('perf-restore', 'Restore owned capture launch configuration exactly.', 'No arguments.', 'Owned launch journal; changed external files cause refusal.', 'Restores or removes temporary Proton user_settings.py; no game request.', 'restored=true and cleanup receipt; does not unload a layer from an already running game.', ['perf-restore'])command('perf-start', 'Start a bounded presentation trace with independent deadline cleanup.', '--seconds 0.1..120 --arm LABEL --threshold-ms N [repeat] [--budget-ms N]', LIVE + ' perf-status available.', 'Arms fixed 65536-record memory buffer; watchdog exports at deadline or game exit. No per-frame JSON/network/disk writes.', 'Artifact folder, deadline and context. Use perf-stop early; partial/empty/error captures are explicit.', ['perf-start --seconds 30 --arm baseline --threshold-ms 25 --threshold-ms 50 --budget-ms 16.67'])command('perf-stop', 'Stop and export the current presentation trace.', 'No arguments.', 'A capture receipt, even after game exit or bridge uncertainty.', 'Disarms memory capture, retains committed prefix, raw trace, CSV, markers and report.', 'Completeness, drops, median/p95/p99/max/spikes, source semantics and errors. Marker exhaustion still exports a failed partial report and finishes; repeated stop returns that report.', ['perf-stop'])command('perf-mark', 'Mark an experiment window or exclusion in the host clock domain.', 'NAME [--phase begin|end|event] [--kind KIND]', 'Active capture.', 'Appends host marker, never runs a game callback.', 'Marker acknowledgement. Use unique paired names; completed-interval summaries exclude crossing intervals; coverage separately exposes boundary stalls and censored gaps.', ['perf-mark gameplay --phase begin --kind active_gameplay', 'perf-mark gameplay --phase end', 'perf-mark collection --kind exclusion'])command('perf-window', 'Measure sustained gameplay with warm-up and optional pause/resume.', '--seconds 1..60 --warmup 0..20 --arm LABEL --threshold-ms N [--budget-ms N] [--pause-seconds 0..10]', PAUSED + ' Qualified capture provider. Fixture and actor count prepared beforehand.', 'Resumes once and runs uninterrupted wall-time window, then pauses. Optional marked pause and one-second resumed window. Boundary checks cannot rule out every internal UI/death transition.', 'Raw trace/report plus distinct warmup/gameplay/pause/resumed summaries and fixture/settings evidence. No tick-step loop; does not select policy or alter actors.', ['perf-window --seconds 20 --warmup 3 --arm baseline --threshold-ms 25 --threshold-ms 50 --pause-seconds 2'])command('perf-compare', 'Compare repeated baseline/treatment performance reports offline.', 'PLAN.json', 'Reports with matching graphics/settings and explicit comparison controls.', 'Reads reports only; never runs a treatment.', 'Per-window distributions and paired deltas; refuses mismatched controls, incomplete traces, missing windows, failed coverage rules, reused captures (including copies), overlapping windows or fewer than two repetitions.', ['perf-compare /path/to/plan.json'])GUIDES['performance'] = """Optional host presentation timing
Build: python3 presentation/build.py (Vulkan-Headers v1.3.290 under research/repos).Close game, perf-launch --proton ACTIVE_PROTON_DIRECTORY, wait for real window,attach, then perf-status. No active provider is an explicit error. Capture uses afixed 4 MiB buffer with a 120-second maximum and host monotonic timestamps.This observes vkQueuePresentKHR entry cadence in the Proton Vulkan chain, not GPUcompletion, displayed frames, script CPU time, or end-to-end input latency.
Load an exact fixture, pause, prepare and qualify the declared actor population.perf-window --seconds 20 --warmup 3 --arm baseline --threshold-ms 25 --threshold-ms 50runs uninterrupted gameplay. No per-frame bridge calls or JSON. For custom flows,perf-start / perf-mark NAME --phase begin|end --kind KIND / perf-stop. Mark menus,loads, compilation, pauses, warmup, collection and active gameplay separately.Native command markers record host request/response bounds, not exact action onset.Unmarked intermediate transitions cannot be inferred from boundary snapshots.
Every artifact retains raw binary/CSV, markers, settings bytes/hashes, process,session/generation and provider build. Percentiles use nearest rank. Spikes arestrictly above user thresholds; adjacent spikes form episodes; report frame-timesum and excess separately. p99 tail qualification requires >=1000 intervals.Raw spikes are never deleted. Only intervals wholly inside declared windows enterwindow summaries. Each window also reports coverage: known overlap, uncoveredleading/trailing time, boundary intervals and censored gaps (never invented frames).Capture complete=true means capture integrity, not performance acceptance.Comparison requires 100% known interval coverage, some completed intervals, andboundary intervals <= the smallest declared spike threshold. This explicit rulealso applies to legacy reports by recomputing coverage from raw data.Use identical capture settings between arms; a frame cap canhide headroom loss. No causal attribution follows from one spike or unchanged FPS.
Prepare repeated baseline/treatment windows with identical graphics/cap, fixture,actor counts, warmup and camera/input protocol; declare the sole changed input inthe comparison plan. Use distinct captures per arm/repetition: copied paths orreused capture identities and overlapping windows are rejected. Disjoint windowsfrom one capture also cannot masquerade as independent trials. perf-comparereports matched-window deltas, not causality.Do not recompile inside measured windows. Detailed combat hooks remain installedon this build; record that shared cost instead of claiming they are disabled.
perf-stop works without a healthy bridge; deadline/game-exit watchdog preservespartial output. A full marker log cannot prevent finalization: retain raw/CSV,export a failed partial report with bounded marker_log evidence, and finish thecapture. perf-stop then returns the same report; a new capture is allowed.Empty captures, overflow and in-flight Present are explicit errors.Stop the game normally and perf-restore; restoration refuses externally editedconfiguration. A crash may leave a journal: inspect it and run perf-restore.perf-verify REPORT.json independently checks raw/CSV/arithmetic integrity offline.See PERFORMANCE.md for exact source semantics, limits and qualified live workflow."""
command('perf-verify', 'Verify raw trace, CSV and recomputed timing summaries offline.', 'REPORT.json', 'Sidecar trace.bin, frames.csv and capture.json beside the report.', 'Read artifacts only; no game process required.', 'Raw hash, ordering, CSV equality, interval-duration consistency and whole/window summary checks. Integrity is not proof of gameplay validity.', ['perf-verify performance-example/report.json'])
command('workspace-status', 'Inspect KDE workspaces, the owned rule and actual Witcher window placement.', 'No arguments.', 'KDE KWin 6 session; Python dbus and gi modules. Game may be closed.', 'Brief read-only compositor query; no game callback, focus change or workspace switch.', 'rule_enabled/target_exists and observed windows. placement_verified requires a live matching window solely on the pinned workspace; false with game closed.', ['workspace-status'])command('workspace-pin', 'Keep Witcher on the existing second KDE workspace across launches.', 'No arguments; targets the second workspace, normally Desktop 2.', 'KDE KWin 6 with at least two workspaces; kwinrulesrc must be writable.', 'Backs up rules and installs one owned Force-desktop rule for steam_app_292030 with maximum focus-stealing prevention; upgrades unchanged legacy rules in place. Applies to existing windows and all future launches, including perf-launch. Does not switch your current workspace.', 'Persistent workspace UUID, owned-rule receipt and live placement when present. Refuses externally edited owned rules. Does not launch the game.', ['workspace-pin'])command('workspace-show', 'Switch to the pinned workspace and focus the game for inspection.', 'No arguments.', 'An enabled workspace pin and existing target workspace.', 'Explicitly switches your visible KDE workspace; focuses the main Witcher window if present.', 'Observed current workspace must match the pinned target. Game readiness and rendering cadence are separate.', ['workspace-show'])command('workspace-unpin', 'Remove only the lab-owned KDE workspace rule.', 'No arguments.', 'Owned rule receipt, unchanged owned rule contents.', 'Removes that rule and its order entry, preserving unrelated window rules. Does not move the existing window or switch workspaces.', 'removed=true; later launches follow ordinary KDE placement. Refuses externally edited owned rules.', ['workspace-unpin'])GUIDES['workspace'] = '''Use the normal KDE Desktop 2 workspace for the game.
launch and perf-launch automatically enable 1080p windowed/background mode andpin Desktop 2 before asking Steam to start. No separate setup commands are needed.stop waits for the game to exit, then automatically restores recorded personaldisplay settings, removes the background adapter and removes the workspace pin.The next Steam Play uses your personal setup; the next lab launch re-enables labmode. If you close/crash the game outside stop, run reset-game-settings once it isclosed (and perf-restore for a performance launch). There is no exit watcher.
workspace-pin installs one persistent KDE window rule, matching steam_app_292030,with Desktop 2 forced by its stable workspace UUID. It moves an existing game andapplies on every future Steam/devkit/perf-launch startup. This is the ordinaryworkspace switcher in your desktop session. No separate display/server/session.Desktop 2 must already exist; setup will not create or rearrange your workspaces.
workspace-status reads actual KWin placement, even while an experiment owns thegame-control lock. rule_enabled is configuration, placement_verified is anobserved live game window solely on that workspace. A closed game has no verifiedwindow. X11 wmctrl/xdotool desktop numbers can be wrong under Wayland; use thisKWin query for placement. Window IDs are KWin UUIDs, not X11 numeric IDs.
workspace-show explicitly switches to the game's workspace for inspection.You can also use KDE's normal workspace switcher. The rule persists when youleave Desktop 2 and when the game closes. It does not put the game on all desktops.workspace-unpin removes just the owned rule, preserving other KDE rules; thecurrent window stays where it is. Externally changed owned rules are not overwritten.The machine-specific journal is workspace-rule.json; backups are under artifacts/.
Placement does not guarantee full-rate rendering while a workspace is hidden.Foreground-sensitive gameplay/performance work may need workspace-show first;changing visible workspaces during a measured run can invalidate its conditions.Desktop screenshots capture the visible workspace, so show Desktop 2 before capture.Python dbus/gi and KDE KWin 6 are required only for these workspace commands.'''
command('background-enable', 'Keep the lab game running when you switch away from Desktop 2.', 'No arguments; applies 1920x1080 windowed preset.', CLOSED + ' Local llvm-mingw toolchain and existing ASI loader.', 'Builds/installs optional PathBackground.asi and changes only DX12 Viewport Resolution/FullScreenMode. Journals previous values. No combat scripts or existing native modules changed.', 'enabled=true, settings and ownership receipt. Requires next launch; background-status distinguishes installed from mapped in the actual game.', ['background-enable'])command('background-status', 'Inspect optional background mode, mapped module and stored display settings.', 'No arguments.', 'Configured local installation; game may be closed.', 'Reads owned files, process maps and settings; no game mutation or focus change.', 'enabled, mapped_in_game, windowed and settings. Mapping is not by itself proof that simulation advances.', ['background-status'])command('background-disable', 'Remove background mode and restore its previous display options.', 'No arguments.', CLOSED + ' Unchanged owned ASI and display options.', 'Removes only PathBackground.asi; restores previous Resolution/FullScreenMode while preserving unrelated settings. Workspace pin remains.', 'enabled=false and restored_viewport. Refuses externally changed owned files/options.', ['background-disable'])command('reset-game-settings', 'Restore your personal display, focus-loss and desktop behavior after lab work.', 'No arguments. Run stop first if the game is running.', CLOSED + ' Configured local installation. Active background/workspace ownership receipts must match current files/options. KDE KWin must be reachable when removing an active pin.', 'Prechecks both ownership records, then runs background-disable and workspace-unpin under the lab lock. Restores recorded Resolution/FullScreenMode, removes PathBackground.asi and the owned Desktop 2 rule. Steam Play uses personal settings; launch/perf-launch automatically enable lab mode again. Preserves saves, combat mods, the devkit bridge, unrelated graphics settings and KDE rules; does not reset factory defaults or performance-capture configuration.', 'background_enabled=false, workspace_pinned=false and per-step results including restored_viewport when changed. Already disabled/unpinned is a successful no-op. No receipt means no invented display defaults. External edits refuse restoration; inspect the named receipt/backup. An I/O failure reports completed steps and the failing step; it may leave a partial restoration.', ['reset-game-settings', 'help reset-game-settings'])GUIDES['workspace'] += '''
For independent manual setup, close the game and run background-enable.It builds an optional PathBackground.asi using the existing ASI loader and setsDX12 to a 1920x1080 window. Then launch/attach normally; background-status mustshow mapped_in_game=true and workspace-status must verify Desktop 2 placement.Installed source/EXE/library hashes and previous display values are journaled.
The focus adapter prevents automatic focus-loss pausing, while allowing normalpause menus and call pause. It suppresses hidden keyboard/mouse input and cursorwarps/capture; native virtual-controller commands still drive the hidden game.No repeated resume/menu-closing loop is used. DoNotPauseWhileStopped was testedand did not prevent focus-loss pausing; it remains at its previous false value.Use reset-game-settings with the game closed to restore your personal setup:recorded resolution/display mode, ordinary focus-loss behavior and KDE placement.It checks both ownership records before changing either and preserves saves, mods,the bridge and unrelated settings. Repeating it after success is a no-op.This restores recorded preferences, not factory defaults; it does not guess displayvalues if no background-mode receipt exists. Inspect help reset-game-settings forexternal-edit refusal and partial-failure recovery. Performance capture has its ownperf-restore command. For independent restoration, background-disable restores thetwo display keys and removes the optional adapter; workspace-unpin removes the pin.
Do not compare these windowed/background runs with earlier fullscreen performancebaselines. Simulation continuity and foreground presentation timing are differentcontracts. perf-window still requires its documented foreground conditions.'''
GUIDES['setup'] = """Standalone Witcher 3 devkitWorktrees share one installation state, selected by the resolved game directory.Source and build outputs stay local; receipts, locks and artifacts live underXDG_STATE_HOME/witcher3-devkit/INSTALLATION_ID (default ~/.local/state).Config discovery: W3DEV_CONFIG, then XDG_CONFIG_HOME/witcher3-devkit/local-config.json(default ~/.config), then checkout-local local-config.json.Configuration uses schema_version 1, game_directory anddocuments_directory. W3DEV_CONFIG selects a config; W3DEV_GAME andW3DEV_DOCUMENTS override directory values. Relative configured paths resolveagainst the config file. Never switch configuration during a live session.A source clone supports help/tests without the game or the combat-lab repository.Run doctor for effective installation paths/tools and verify for owned hashes.Configuring paths is not installation: install-native/install-ui are updatesto an existing owned installation, not first-time installers. See SETUP.md.Migrate closed-game legacy state with migrate-state --from CHECKOUT (read-only plan),then add --apply. This moves receipts, never copies ownership; historical artifactpaths remain aliases. Use upgraded source after migration, not old CLI revisions.Sockets use XDG_RUNTIME_DIR when set, otherwise private installation state.Downloaded tools can use XDG_CACHE_HOME/witcher3-devkit; builds stay worktree-local.Never change XDG roots/config during a session. All worktrees must use the same roots.An owner records source hashes; changed-source mutations are rejected. Recoveryuses the unchanged original checkout. Do not edit/remove owning source mid-session."""
command('migrate-state', 'Move closed-game legacy ownership state into shared XDG storage.', '--from CHECKOUT [--apply]', 'Configured existing installation; game and broker closed; same filesystem; no conflicting destination.', 'Without --apply: read-only plan. With --apply: move receipts and lock inodes, retain artifact aliases, publish global config. No game launch or deployment.', 'applied, state and configuration paths. Partial failure retains migration.json; retry the same source, never copy receipts or remove locks. Old source revisions must not operate after migration.', ['migrate-state --from ~/witcher3-devkit', 'migrate-state --from ~/witcher3-devkit --apply'])