This is the operational queue for open-ended VirtualDJ reference work.
Agents should start here for maintenance, cleanup, documentation, and evidence-pass tasks. Pick the first Ready task unless the user names a different task. Read the task-listed files before running broad repository searches.
Ready: startable now with the listed files and fixtures.Blocked: needs hardware, a live VirtualDJ setup, or a clearer external source.Parking lot: useful later, but not the next best use of time.- Record manual VirtualDJ observations in docs/VDJScript Local Test Tracker.md.
- Promote stable conclusions into the topical docs named by the task.
- Run
just checkafter documentation, fixture, or status edits. Read firstlists are section-scoped: read only the named rows/sections. Usejust grep-verb-docs <name>for verb lookups instead of openingdocs/VDJScript Verbs.md.- docs/VDJScript Reference Consolidation Plan.md and docs/Completeness Roadmap.md are frozen design references. Do not refresh, reorder, or re-scope them; this file is the only active queue.
Status: Foundation landed (2026-07-22) — generation + migration remain
The store and its query/edit API exist and are wired into just check. This is the compounding-cost reducer: it replaces the record-in-tracker-then-promote-to-three-docs cycle with one just put-verb, and lets agents query verb state without loading the 6,300-line monolith.
Done in this pass:
- tools/verbdb.py over the authoritative store docs/vdjscript-verbs.json, fronted by
just get-verb / put-verb / find-verbs / next-incomplete-verb / verb-stats. Storage is private behind the API so it can later become one-file-per-verb without retraining agents. - Merge-safe
bootstrapseeded all 991 records from the index + coverage audit (official names + Needs-Local-Test gap) + tracker status tables. It correctly finds the 19-name gap (17 hardware-blocked → skipped bynext-incomplete), leavingdualdeckmode_decksandsystemas the 2 active items, and auto-detected 7 trackerPassrows. verbdb.py check(schema, alias resolution, index coverage, count freshness) is injust check. Entrypoints (AGENTS.md,INDEX.yml,docs/README.md,tools/README.md) route verb lookups and result-recording to the flatjust get-verb/find-verbs/put-verbcommands.
Reports are queries, not files (2026-07-22):
just find-verbsfilters on--surface,--section,--tier,--status,--kind,--needs-test, with--format=jsonfor structured output and--limit. A category listing is just an unfiltered query, so no derived Markdown is written to disk — nothing can drift, and there is no staleness gate to maintain. An earlier pass generateddocs/VDJScript/generated/*.mdand was reverted for exactly this reason.- Rule for future work: do not add a generator that writes a Markdown copy of store data. If a view is wanted, add a query or a flag. Building reader-facing documentation is a later phase, driven by findings — not something to design for now.
Remaining:
- Add richer record fields as needed by contracts (
forms,platforms,deck_scope);putcurrently covers the scalar/list fields, nested contract detail is hand-edited in the JSON. - Grow the query layer where a real question is awkward to ask (e.g. verbs by evidence source, or by presence of a local-test note).
- The monolith still holds the authored prose. Retiring it follows the frozen plan's phased, one-family-at-a-time migration; do not delete hand-authored docs ahead of that.
Effect catalog is queryable (2026-07-22): tools/fxdb.py / just get-fx / find-fx / fx-stats answers slider/button questions straight from the sweep artifact, gated by fxdb.py check in just check. No Markdown copy — same rule as the verb store.
Tasks 1-4 are one FX cluster: they share the same VirtualDJ session and the same deck-FX context. Batch them into one local-test session where possible. Preferred readback channel: the HTTP control interface (just vdj-query), which returns exact strings and makes the sweeps scriptable — the older name=-interpolation pad technique (proven on v2026-m b9482) is now needed only for pad/skin-surface-specific checks.
Status: First cut landed (2026-07-26) — coverage tagging remains
The done-when is met: just topic <term> (tools/topic.py) answers a topic
question with matching verbs, effects, and XML elements and the real example files that
use them (grep-verified, ranked by coverage), plus topical docs and local-test quirks. It
is pure aggregation over the already-gated stores — no artifact, no hand-tagging — deriving
everything from verb section, inventory families, and word-boundary grep. Wired into
AGENTS.md, INDEX.yml, and just check (cross-store smoke test).
Remaining — the metadata layer, which is the part that needs real tagging:
- Topic reach for name-opaque items. An item is only found under a topic if the topic
appears in its name, section, or a grep of it. That misses families whose topic is not in
the element name — searching
waveformdoes not surfacerhythmzone,scratchwave,blockwave,beattunnel(only the doc pointer saves it). These need an explicit topic tag. This is the "rich metadata to each searchable item" idea, and it is good mechanical subagent work: add atopics: [...]field to store records and an element→topics map, then havetopic.pyconsult it alongside the derived matches. - Multi-word terms are treated as one string (
color fx≠colorfx); a synonym/alias map would fold those together. - Keep it a query — no generated topic pages. Same rule as everywhere else.
Read first:
- tools/topic.py — the aggregator to extend with a tag lookup.
Status: Structural sweep COMPLETE (2026-07-22) — only rendering behavior is left
tools/sweep_fx_introspection.py captured counts, short+full labels, normalized defaults, live value text, and length/beats flags for all 119 installed effects into tests/fx-introspection-dump.json, plus the enabled cycle for all three targets. Query it with just get-fx <effect> / just find-fx [--category=deck_fx|video_fx|transition] [--has-length] / just fx-stats — do not read the dump and do not hand-transcribe it.
What the sweep settled:
- Introspection is read-only. Every
get_effect_*helper accepts an effect name where the docs show a slot number (get_effect_slider_count 'Echo',get_effect_slider_default 'Echo' 3), returning the same values as the slot form for all 119 title-resolvable effects with noeffect_selectand no state change. This is the cheap way to ask about an effect that is not loaded. get_effect_title '<name>'returns'<Canonical> - Deck N'or'', so it resolves a name to its canonical spelling and probes existence. Case-insensitive, not space-insensitive. Blind spot:''forStemsandVocals, which select and introspect fine through a slot — so a title miss must be confirmed by selecting before the name is called unknown.- Audio-vs-video: cycle membership, not loadability. All three selectors accept any installed effect name (
video_fx_select 'Echo'really does set the video slot to Echo), so what a target accepts discriminates nothing. The three+1cycles are disjoint and are the app's own category assignment: 63 deck FX, 17 video FX, 35 transitions. Each is the enabled/favorites subset, so an installed effect in no cycle (Lottery,Sweep,Title,Vocals) is category-unknown, not uncategorised. BrakeandShaderresolved.Brakeis not a selector name on this build at all — a docs-catalog error; the real ones areBrakeStart,VinylBrake,Beat Brake.Shaderis an alias forVisuals, which loads into a deck slot perfectly well; the original sweep only ever asked for it by the wrong name.BeatGridis likewise a spacing error forBeat Grid. Nothing here was ever "video-only".*_skip_lengthre-indexes, it does not blank. Index i is the i-th slider with the length slider removed, so the last index is always empty. Verified on all 47 length-bearing effects; the length slider is not always index 2 and not always labelledLEN.
Remaining (rendering behavior, needs video output — not introspection):
video_fx_slider,video_fx_clear,video_transition_slider, anddeck masterscoping: what they actually render.- Whether the 4 category-unknown effects land in a target's list when enabled in the FX list editor.
- Promotion stays deferred to TODO task 0: the data is queried from the artifact, not copied into
Effects Engines.md. Do not hand-transcribe the dump.
Start here:
- Re-run after a VirtualDJ update:
python3 tools/sweep_fx_introspection.py > tests/fx-introspection-dump.json(needsjust vdj-upgreen) - tests/Pads/Reference - FX Introspection Test.xml (pad-surface checks only)
Read first:
- docs/VDJScript Local Test Tracker.md (FX Helpers rows only, for the established method)
just get-fx <effect>instead of docs/Effects Engines.md for control maps
Record results in:
- docs/VDJScript Local Test Tracker.md, and
just put-verb <name> test_status=… evidence="…"
Done when:
- The remaining video-rendering verbs get at least one recorded pass each.
- Generic FX control guidance distinguishes observed behavior from inference.
Status: DONE (2026-07-26, HTTP). A bank is a rack of effect SELECTIONS for slots 1-6 — not active state, not slider values, and global across decks. effect_bank_load returns true/false as a bank-populated probe. Recorded in the tracker and on effect_bank_save/effect_bank_load (just get-verb effect_bank_save).
Start here:
Read first:
- docs/Effects Engines.md (bank save/load rows only —
rg -n effect_bank) just grep-verb-docs effect_bank_save
Record results in:
Promote to:
Done when:
- Restored effect names, active states, slider values, and deck scope are recorded.
Status: PARTIAL (2026-07-26, HTTP). Confirmed the release-FX path is separate from deck slots 1-6 (is_releasefx never flips from loading effects into numbered slots); the release sliders are accepted but inert without an armed release FX, which needs a momentary control HTTP can't drive. Remaining: arm a release FX on a pad/mapper surface and characterize activation. Recorded in the tracker and verb store.
Start here:
Read first:
- docs/Effects Engines.md (release-FX rows only —
rg -n releaseslider) - docs/Effects Usage.md
Record results in:
Promote to:
Done when:
effect_releaseslider*andis_releasefxbehavior is described separately from normal deck FX controls.
Status: DONE (2026-07-26, HTTP). Confirmed plugin-instance-scoped (targets the BeatGrid slot), with a bare form and an unquoted-slot-number form; get/set/cur are BeatGrid's own vocabulary. Recorded as BeatGrid-specific, not generic. See just get-verb effect_command.
Start here:
Read first:
- docs/Effects Engines.md (BeatGrid and
effect_commandrows only —rg -n effect_command) - docs/Native Effects.md
Record results in:
Promote to:
Done when:
- Confirmed
effect_commandexamples are documented as BeatGrid-specific rather than generic plugin control advice.
Status: MAPPER FIRING DONE (2026-07-27, DDJ-GRV6 hardware) — device-definition schema still open. HTTP-verified on real hardware that the mapper <map value action> schema binds and fires (ONINIT on load, PLAY_PAUSE on press), plus three gotchas: control names must match the device definition exactly (wrong name fails silently), loading a mapping resets $ globals, and editing an active mapper file needs a full restart (re-select does not reload). See the tracker's "Mapper Firing" section and docs/Mapper XML.md. Factory-mapping export (Factory default -> Save) was tried as a shortcut to the device definition: it yields the factory <mapper> (control names + canonical actions, 293 bindings, lints clean) but NOT the <device> definition, so it does not unblock this. STILL OPEN: the custom <device> definition schema is untested because the DDJ-GRV6 is factory-recognized — needs unrecognized hardware or a virtual MIDI port + injection to exercise a custom device definition.
The mapper reference's device-definition schema is official-doc-derived but never load-tested locally. A SIMPLE_MIDI device context already exists in the local install's Mappers folder. Mappers are one of the repo's named coverage cliffs, so this is the highest-value task outside the FX cluster.
Start here:
Done when:
- A minimal
<device type="MIDI">XML placed in the VirtualDJDevices/folder is detected by the app, and a paired mapper's<map>bindings fire. - Results (including failures) are recorded in docs/VDJScript Local Test Tracker.md and promoted into
Mapper XML.mdsource labels (Local test). - RESOLVED 2026-07-27 for two of the three mapper-lint warnings (and note these were never factory-sourced: all three came from personal local mappings, mis-graded as factory by the now-corrected
author-attribute rule (the tag does not track authorship) in examples/Mappers/README.md):browser_filterandbrowser_searchare not verbs on this build — noACTION_symbol and no bare string anywhere in the executable, no Button Editor autocomplete,E_FAILover HTTP. The lint warnings are correct and the mapper lines using them do nothing;clear_searchis the real verb.noneremains unresolved (noACTION_symbol and no autocomplete, but "none" is an English word so its presence in the string table proves nothing); its only observed use is as a do-nothing LED placeholder. See the disproof method in docs/Undocumented VDJScript Candidates.md.
6. Continue Hidden Button Editor Candidate Probes
Status: Ready — reframed 2026-07-29: these are no longer "candidates". All 37 hidden names are
proven real by verb-table membership (flags == 256), every one now has a verb-store record,
and 34/37 have HTTP-proven kind. What this task probes is behavior only.
Start here:
Read first:
- docs/Button Editor Catalog Audit.md
- docs/Button Editor Taxonomy.md
- docs/VDJScript Local Test Tracker.md (hidden-candidate probe table only)
Record results in:
Promote to:
- docs/VDJScript Verbs.md, only when behavior is locally observed and useful enough for normal guidance.
Done when:
- Candidate behavior is recorded as pass, partial, failed, or still discovery-only.
- Catalog-only names stay out of ordinary recommendations unless behavior proof supports promotion.
Status: Ready, but low expected yield until a concrete context is identified
The first pad-context run (v2026-m b9336) recorded dualdeckmode toggling on while current and deck-scoped dualdeckmode_decks readbacks stayed false on both decks. The promotion condition is a visible dual-deck pair or controller context (deck pairs 1/3 or 2/4), which realistically means a 4-deck skin setup or a controller. Do not repeat the same pad-context probe; identify the better context first, or treat this as semi-blocked.
Start here:
Read first:
- docs/Official VDJScript Coverage Audit.md (the
dualdeckmoderows only)
Record results in:
Promote to:
Done when:
- A build, deck/controller context, observed result, and follow-up decision are recorded.
- The official local-test status is updated only if the repeat confirms behavior well enough for promotion.
Status: DONE (2026-07-27) — transport, wire format, subscriptions, and actions are all verified in both directions; only minor open questions remain (see end of this task)
Settled with a live session (socket watcher + dns-sd + per-connection nettop deltas;
recorded in the tracker, docs/HTTP Control Interface.md,
and docs/Application Internals.md Remote Skins):
- Remote does not use the Network Control HTTP channel; port 80 saw no Remote traffic.
- Discovery is inverted from the obvious guess: the phone advertises Bonjour type
_vdjremote8._tcp(SRV → phone, port 4243 observed) and listens; VirtualDJ connects out to the phone as the TCP client, one persistent connection. - Semantics are event-driven push: idle seconds carry 0 bytes on that connection; a deck load pushed ~249 KiB desktop→phone in one second with no inbound request; unload ~1.4 KiB; otherwise only sub-KB keepalives.
Wire format also DONE (2026-07-27) — see docs/Remote Protocol.md.
Framing is 8JDV + u32 total length + u16 type; the device opens with subscription
frames carrying ordinary VDJScript queries by id, and VirtualDJ pushes typed values
(val float32 / txt / fail) plus browser folder XML, settings, and selected-folder
state. Replaying a captured opener is enough to hold a session — no pairing token. Capture
tool: python3 tools/vdjremote_dial.py <device-ip>; reference capture at
tests/vdjremote-opener.bin.
Subscriptions also DONE (2026-07-27): the vocabulary is all of VDJScript, not a fixed
schema. Verified by substituting synthetic SUBSCRIBE frames into a replayed opener —
get_version, get_effect_name 1, deck 3 get_bpm, and a full ternary all resolved, and
push-on-change was measured (a load pushed title/artist/BPM/path within the same second;
get_position streamed at 33-34 Hz while playing, silent when paused). KIND is a hint, not
a request; fail means "no value now", not "bad query". Tool:
python3 tools/vdjremote_subscribe.py tests/vdjremote-opener.bin 'left:get_title' 'get_clock'
paired with a dns-sd -R advert.
Remaining:
- Mid-session subscribe/unsubscribe is untested; only opening-burst registration has been exercised. A client that switches views needs it.
- Map or bypass the
0x02control id space. Only four ids are known, all from one device skin (0xc6play,0xc7cue,0x41crossfader,0x36volume). Whether the space is global or skin-defined is open — but0x31may make it moot for third-party clients. - Mid-session subscribe/unsubscribe is untested — only opening-burst registration has been exercised. A client that switches views needs it.
- Undecoded types: device→desktop
0x09,0x0c,0x27,0x29,0x34; desktop→device0x2b,0x3b. Sessions work without understanding them (replay reproduces them), so this is lower priority. - Waveform data has not been located in any frame; check inside the
0x25ZIP payloads.
Record results in:
Promote to:
Done when:
- Action frames are catalogued with example payloads, and the subscription vocabulary is characterized as either "any VDJScript query" or a documented subset.
Status: DONE (2026-07-27) — verb set, aliases, hidden flag, and categories all extracted
The exact verb set is settled: tools/extract_verb_table.py
extracts VirtualDJ's own verb table (1,028 sorted 16-byte records
{const char *name; uint32 id; uint32 flags} at 0x10402d020), covering 1,007/1,007
HTTP-proven names. Existence and non-existence are now a membership test
(just verb-table <name>), aliases are read off shared ids, and flags == 256 marks the
37 Button-Editor-hidden names. 61 alias groups are written into the verb store.
Categories are solved too: a const char *[38] name array after the verb table, plus a
non-decreasing uint8[956] in __TEXT,__const giving the category per verb indexed by
id + 1. Both located structurally, no pinned addresses. Confirmed by the live Button Editor
list (read off screen, flow..video with defines skipped), and reproducing
docs/Button Editor Taxonomy.md's per-category counts
exactly — all 37 categories, total 1,028. See
docs/Undocumented VDJScript Candidates.md
§Categories.
Note (2026-07-29) that the count agreement is a reproduction, not corroboration:
extract_vdjscript_taxonomy.py reads the same three structures, only by pinned address instead
of by anchor. Rule 1c2 in docs/Evidence Standards.md now says so.
The live Button Editor remains the only independent check.
Four-verb residue: RESOLVED as doc error. mute, silent_cue, stems_split, and loaded were
listed under different categories in the taxonomy doc's example column than the mapping gives.
That column is hand-written, not extraction output: the extractor emits
visible_examples[:8] over an alphabetically sorted table, yet every doc row has exactly 4
entries and four rows are not even alphabetical (play, pause, stop, silent_cue;
mic, linein, aux_volume, mic_eq_high; leftdeck, rightdeck, masterdeck, pfl;
beatjump, clone_deck, dualdeckmode, mute). The counts column is extraction output, so the
two columns have different pedigree and only the counts carry weight. Mapping stands; the doc's
example column is corrected in place.
Status: DONE (2026-07-29) — both "done when" conditions are met: every HTTP-proven name is accounted for by a named structure (the verb table covers 1,007/1,007), and every structure-found name absent from the store has been added (the 35 hidden verbs, 2026-07-29). Kept because the corroborating sources are still wired.
Three structures are extracted so far (tools/extract_binary_verbs.py,
1,019 names): 954 ACTION_ implementation classes, 812 language-catalog entries, and the
parser's 967-entry alphabetically sorted name table. Their union covers 998 of the 1,007
names the HTTP sweep proved real.
Four names prove at least one more structure exists: browser, config, preview, and
volume are real verbs, are ≥4 characters (so strings sees them), and appear in none of the
three sources. (jog, no, off, on, yes are also missing but are simply under the
strings length floor — use strings -n 2 for those, and note a low minimum breaks the
sorted-run detection, so probe them separately.)
Why this is worth doing: the app must match input against a complete set of names, so the sets exist in the binary. Recovering all of them yields the exact verb set — including any undiscovered verbs — which is the strongest possible outcome for this topic, and it lets the disproof drop its conservative string leg (see docs/Evidence Standards.md rule 1a).
Start here:
- Locate
browser/config/preview/volumeoccurrences and inspect their neighbourhoods for table structure, as was done to find the sorted name table. - The sorted table runs
action_deck…zoom_verticaland omitsload,loop,cue,hot_cue,nothing— so it is a subset dispatcher, and whatever holds those five is another candidate structure worth characterizing even though the union already covers them. - Both older extractors are stale on this build (
extract_vdjscript_symbols.pyreturns 0;extract_vdjscript_taxonomy.pyis address-pinned) — re-anchoring the taxonomy tables would recover the Button Editor's own category structure, which is a likely home for the strays.
Done when:
- Every HTTP-proven name is accounted for by a named structure, or the residue is explained.
- Any name found in a structure but absent from the store is swept (
just verb-probe) and recorded as a candidate.
Status: Ready (2026-07-29) — the ratified priority now that existence, aliases, hidden flag, and categories are settled. Goal: for every verb, the complete calling contract — query return type, accepted argument forms, and undocumented overloads — established at Tier 1 where possible and recorded as structured per-verb data, not prose.
Contract fields to establish per verb:
- Return type in query position: HTTP leg DONE (2026-07-30) —
tools/sweep_return_types.py sampled all 652 query verbs in
three read-only contexts against live VirtualDJ: 623 typed (334 bool / 145 int / 68 float /
4 percent / 72 text), 0 capability conflicts vs the structural matrix, gated in
just check, queried withjust verb-return-type <name>. It also refuted the first slot labeling: slot 3 is a generic variant query, not onQueryBool — structure claims capability, observation claims type. Remaining sub-legs: the 29 all-empty verbs need richer contexts, and the Remote-protocol typed sweep (valfloat32 vstxtVALUE frames) would confirm wire-level types independently of HTTP text rendering. - Argument forms — binary-first (reprioritized 2026-07-30, partially DONE). The
E_INVALIDARG method fingerprint landed in the contracts artifact: 436 verbs demand an
argument somewhere, 114/116 agreement with the sweep's needs-args kind, and 301
bare-answering query verbs flagged as taking OPTIONAL arguments — the
undocumented-overload queue (
summary.optional_arg_queries), invisible to the bare sweep.method_stringsrecovers per-method keywords (loaded→opposite,get_time→short). Remaining streams for the forms and types:- Argument keywords — DONE (2026-07-30); types — closed as not-extractable.
__stubsare now named via DYSYMTAB's indirect symbol table (this build is classicLC_DYLD_INFO_ONLY, not chained fixups), so methods that compare an argument against literals are detectable: 259 verbs carrykeyword_candidates, recovering undocumented enums (get_bpm absolute|ghost,browser_window sidelist|karaoke|…,loaded opposite,get_time short,sampler_group_volume touchrelative|locked). Argument types were attempted through the same route and do not survive: param access is inlined, and a verb's library calls describe what it does with an argument, not how it fetches one (_strcasecmpappears in numeric-arg verbs). Types are a Tier-1 job. - HTTP probe in query position (Tier 1): start with the 301 optional-arg queries and
the recovered keywords. Method note (2026-07-30, probed live): optional-arg verbs
silently ignore unknown arguments (
loaded bogusword→yes), so error codes cannot confirm a form — probes must compare VALUES across forms in a state where they would differ (e.g.loaded oppositewith exactly one deck loaded).get_timedemonstrated the pattern: bare returns one mode, any argument switches to another. Query-position probing is side-effect-free; execute-position confirmation only for whitelisted safe verbs, with independent readback (rule 4), neversystem/file/database verbs. - Shipped-XML corpus mining + appendix parsing (Tier 2): demoted to corroboration of actually-used forms — the binary and the probes discover; the corpus confirms parser-accepted usage.
- Argument keywords — DONE (2026-07-30); types — closed as not-extractable.
- Capability matrix: DONE (2026-07-29) — the vtable-override extraction worked on the
first attempt. tools/extract_action_contracts.py walks
the surviving RTTI (name → typeinfo → vtable) and emits
tests/action-contracts.json: 955 classes ↔ 955 ids as a
checked bijection, capability matrix (execute / bool / number / text query) with slot
meanings calibrated from HTTP-proven verbs, family taxonomy from the class hierarchy
(100 toggles, 56 sliders), and context mixins. Sweep agreement 652/652 (query) and
181/186 (action-only); the 5 outliers surfaced a lead — slot 7 is probably the
options-menu provider (
browser_export,*_optionsoverride only it). Gated injust check; query withjust verb-contract <name>. See the Candidates doc §Contract structure.
Status: Ready, and the main plan for the rest of task 10. Everything above was extracted from the binary or observed through HTTP; both are now at their limits for the questions that remain. The C++ plugin interface is the instrument that clears them, and it is documented in docs/Plugin SDK.md.
Why it is the right instrument, not just another one:
virtual HRESULT SendCommand(const char *command)=0; // execute
virtual HRESULT GetInfo(const char *command, double *result)=0; // numeric query
virtual HRESULT GetStringInfo(const char *command, void *result, int size)=0; // text query- Native types, no flattening. HTTP renders everything to text, which is why return types
had to be recovered by observation and why
master_beat_numshows float bits as an integer. A plugin reads thedoubledirectly — and callingGetInfoon it settles in one call whether that defect is in the core or in HTTP's rendering path. - A definitive type-path map. Calling both
GetInfoandGetStringInfoon each of the 955 verbs, and reading the raw HRESULTs, says exactly which channel each verb answers on — no inference from rendered strings. - Throughput. The 301-verb optional-argument queue and the 259 keyword sets need hundreds of probes each; in-process that is a loop, over HTTP it is hundreds of round-trips.
- Different prerequisites. No Network Control plugin, no Pro license — a fifth Tier-1 channel, which docs/Evidence Standards.md already lists as planned and explicitly forbids claiming anything from until it exists.
Plan:
- Build the minimal plugin read-only:
GetInfo/GetStringInfosweeps only, noSendCommand, so loading it cannot change state. Execute-position testing comes later behind an explicit switch. - Emit the same artifact shape as the existing sweeps (
tests/…json+ ajustquery) so it joinsjust get-verbfor free. - First three experiments:
master_beat_numviaGetInfo; the both-channels type map; then the optional-argument queue, remembering that unknown arguments are silently ignored (rule from 2026-07-30), so probes must compare values in prepared state, never error codes.
Prerequisites and constraints:
- SDK headers are not vendored — no license grant exists. Fetch to
vendor/(gitignored); see the.gitignoreentry and docs/Plugin SDK.md. - Xcode build, macOS arm64. The community examples build under Xcode 14.3 / macOS 13.3.
- Open question to resolve while building: the shipped
beatport16_vdjbundle exports noDllGetClassObject, so a second loading path may exist. A built test plugin settles it. - Also worth probing from inside: whether an internal verb-registration path exists, which is the one scope limit on Evidence Standards rule 1b.
Storage: DONE (2026-07-30) — just get-verb <name> joins the store record with the verb
table, the structural contract, the HTTP existence probe, and the observed return type at
read time (--raw for the bare record). Artifacts stay authoritative; nothing is copied into
the store, so a re-extraction is picked up without a migration.
Done when:
- Every query verb has an observed return type with the observing channel recorded.
- Every verb has an arg-forms record: observed forms, rejected forms, or
no-args. - Overloads (same verb, distinct arg shapes with distinct behavior) are recorded as such.
- The store's per-verb record surfaces all of it through
just get-verb.
- Controller display helpers:
controllerscreen_deck,controller_battery. - Gemini display helper:
gemini_waveform_zoomlevel. - Phase helpers:
phase_movement,phase_position,phase_active. - Numark V7 helper:
v7_status. - Pioneer RZX helpers:
rzx_touch,rzx_touch_x,rzx_touch_y. - DJC-family helpers:
djc_shift,djc_button,djc_button_popup,djc_button_slider,djc_button_select,djc_panel. - Denon platter/display helper:
denon_platter.
- HTML reference export for humans (requested 2026-07-29, deliberately deferred until the contract data exists): generate a static, browsable HTML reference from the verb store + verb table + contracts artifact — one page per verb plus category/alias indexes. Generation only, from the JSON stores (never hand-written HTML copies, same rule as Markdown); becomes worthwhile once task 10 gives the pages real content beyond names.
system: revisit only if an official example, bundled-resource context, or clearly harmless parameter appears.- Skin
visual typecanaries: do after the current no-hardware VDJScript evidence queue unless a skin-specific question makes it urgent.