effort crosswalk - bitcobblers/wod-wiki GitHub Wiki
title: Effort Crosswalk β Entity Γ Storage Γ WQL Γ Runtime date: 2026-10-04 status: review tags: [effort, wql, domain-model, storage, crosswalk]
Review document: what data the effort entity defines and stores today, how efforts are
grouped, referenced, searched, and how they link to WQL. Every claim is anchored to
source (path:line). Implemented behavior only β the drift register in Β§9 lists
gaps and stale claims, and flags anything aspirational in older docs.
Companion docs to reconcile against, not contradict:
Effort.md (entity spec), wql-inventory.html (execution
mechanics), wql-reference.md (filter allowlist).
Single source of truth: types.ts in
@bitcobblers/wod-wiki-lang. The slug is the durable identity boundary; labels and
aliases may vary (comment at types.ts:51-53).
| Property | Type | Req | Meaning | Written by | Read by |
|---|---|---|---|---|---|
id |
UUID string | β | Record id | create/clone paths | storage keys, fixtures |
slug |
string, lowercase-hyphenated | β | Canonical identity; IDB keyPath; route param | derived from label or filename stem | everything (see Β§5) |
label |
string | β | Primary display name | markdown frontmatter, create form | WQL effort: filter, UI titles |
aliases |
string[] | β | Alternative names for matching | frontmatter, "Link as Alias" UI | resolver exact/fuzzy; WQL effort:/text:
|
baseAttributes.met |
number | β | Metabolic equivalent | frontmatter, derivation math | calc effort table, resolved widget |
baseAttributes.discipline |
EffortDiscipline |
β | 10-value vocabulary (Β§3.1) | frontmatter, validated at parse | grouping, WQL discipline:, nav tags |
baseAttributes.disciplineFactor |
number | β | TIS multiplier; resolved from discipline when absent | β |
disciplineFactorFor (disciplines.ts:54-58) |
baseAttributes.intensityTier |
'low'|'moderate'|'high' |
β | Qualitative bucket | frontmatter | WQL intensity:, catalog subtitle |
registrySource |
'bundled'|'user'|'synthetic-unresolved' |
β | Tier of record | SeedImporter / clone paths / resolver | WQL origin:, source badge |
derivation.parentSlug |
slug | β | Clone lineage (user efforts) | auto-clone, explicit clone | resolved widget, resolver materialize |
derivation.coefficients |
Record<string,number> |
β | Multiplicative MET modifiers, gated by modifier keys | clone form | EffortResolver.materialize |
derivation.hardOverrides |
Record<string,unknown> |
β | Attribute replacement; met wins last |
clone form | resolver |
createdAt / updatedAt
|
ISO string | β | Timestamps | upsert paths | β (displayed nowhere) |
body |
markdown string | β | Free-form notes | effort doc editor | detail page note shell |
hints |
Record<string,unknown> |
β | Compiler hints merged onto resolved blocks | frontmatter (rare) |
mergeHints β only 7 consumed keys (hints.ts:68-84); all other keys are inert |
Derived (never stored): ResolvedEffort β effective copy after alias lookup,
derivation walk, modifiers, hard overrides, discipline factor
(types.ts:118-142): effort, definition, slug, label, met,
baseAttributes, discipline, disciplineFactor, intensityTier, modifiers,
registrySource, resolvedFrom ('user'\|'bundled'\|'default'), isEstimated.
| Plane | Location | Shape | Notes |
|---|---|---|---|
| Bundled source of truth |
markdown/efforts/<dir>/<slug>.md (160 files) |
YAML frontmatter + markdown body |
slug == filename stem, enforced by SeedImporter (SeedImporter.ts:14-15). unconventional/ dir holds 14 files whose discipline: is still a canonical value |
| Seed chunks |
public/seed/manifest.json + content-hashed chunks/*.json
|
note-wrapped efforts chunk | fetched no-cache, version stamped via __SEED_VERSION__
|
| IndexedDB |
wodwiki-db v24, efforts store β IndexedDBStorage.ts:262-267 |
full IEffort, keyPath slug; indexes by-discipline, by-source
|
both tiers in one store, distinguished by registrySource. Seed import forces bundled, never overwrites a user row, deletes vanished slugs only while seed-owned (SeedImporter.ts:309-341) |
| In-memory registry |
CompositeEffortRegistry β CompositeEffortRegistry.ts
|
two Maps (user, bundled); user wins on slug collision (:66-69) |
hydrated from IDB at boot; falls back to 5 test fixtures when IDB has no bundled rows (effortRegistry.ts:23-26) |
| WQL | wql-side IEffort β stores.ts:59-72 |
loose structural subset: met?, registrySource widened to string, open index signatures |
adapters bridge with as unknown as casts in three hosts (engine cli/query.ts:25,140, playground queryService.ts:22-23,58-66, storybook journals.ts:89-92) β see drift #1 |
The sole EffortStorageAdapter implementation is
IndexedDBEffortStorage.ts
(delegates to StorageService). No filesystem/localStorage adapter exists despite the
interface doc (types.ts:101-102).
Effort pages double as notes: saving an effort upserts the registry record and
syncs a Note effort/<slug> + markdown segment into IDB
(useEffortContent.ts:155-172).
| Dimension | Vocabulary | Enforced at | Surfaced in |
|---|---|---|---|
| Discipline | 10 values: bodyweight, cycling, gymnastics, kettlebell, recovery, rowing, running, strength, swimming, walking β disciplines.ts:14-25 |
effort-markdown.ts:40-52 drops unknown values at parse (warn, never mints) |
IDB by-discipline index; L2 nav tags (appNavTree.ts:204-226); WQL discipline:; catalog subtitle |
| Discipline factor | strength/kettlebell/gymnastics 1.2, recovery 0.9, else 1.0 β disciplines.ts:41-53
|
resolver materialize |
tis calc |
| Origin |
bundled | user (listable); synthetic-unresolved resolver-only β types.ts:10-20
|
registry upsert rejects non-user writes (CompositeEffortRegistry.ts:108-125) |
source badge, WQL origin:, useEffortCatalog({origin})
|
| Intensity tier |
low | moderate | high β types.ts:23-25
|
free enum on frontmatter | WQL intensity:
|
| Derivation lineage |
derivation.parentSlug self-reference |
auto-clone on bundled edit | "Effective Resolution" widget (EffortDetailPage.tsx:84-89) |
Corpus distribution (verified 2026-10-04, grep '^slug:' count): strength 45,
swimming 23, bodyweight 28, kettlebell 19, gymnastics 18, unconventional 14,
running 4, rowing 3, cycling 3, recovery 2, walking 1 β 160 total.
No per-effort tag/category store exists; discipline tags are the only category-like facet. Directory names are not the vocabulary.
synthetic-unresolved records exist only inside the resolver (default MET 5.0,
never persisted, never listed β EffortResolver.ts:204-224). User efforts are born
two ways: explicit clone (-custom slug, EffortDetailPage.tsx:291-311) or
auto-clone on first edit of a bundled effort (useEffortContent.ts:126-186).
| Referencer | Mechanism | Site |
|---|---|---|
| Script lines |
{type:'effort', value:'Back Squat', origin:'parser'} metric on statements |
EffortMetric.ts (whole file: value = raw text, no identity) |
| Script β effort index |
block_efforts junction rows (noteId, blockContentId, effortSlug), rebuilt at note save; indexes by-note/by-block/by-effort
|
StorageService.ts:538-582, IndexedDBStorage.ts:277-283
|
| Logged events |
EventRecord.effortSlug (+ by-effort index) |
derivation.ts:266-296 |
| Analytics facts |
AnalyticsDataPoint.effortSlug / discipline / intensityTier folded from calc-annotation metadata |
packages/wql/src/derivation.ts:139-171 |
| Journal notes | scheduled workouts: category:'effort', sourceNotePath = effortPath(slug); runs carry noteId effort/<slug>
|
EffortDetailPage.tsx:231-256, noteIdentity.ts:16-22
|
| Alias pages | one Page per alias (e/<alias-slug>, title "<label> (Alias)") |
StorageService.ts:600-616 |
| UI routes |
/e/:slug detail (canonical), /effort/:slug legacy, /efforts catalog |
routes.ts:45-60; ADR-0001 |
| WQL fact plane |
{effort:slug} matches factTagValue β row.effortSlug
|
QueryService.ts:322-330 |
Compile-time identity: EffortEnrichmentPass resolves the block label via
resolver.resolveEffort(label) and stores the full ResolvedEffort as an
effort-data metric in private block memory (metric:tracked), merging effort.hints
onto the block β EffortEnrichmentPass.ts,
called per compiled block from CompileAndPushBlockAction.ts:39-43.
Runtime identity: TwoPassEffortResolutionProcess re-resolves every output statement
carrying an Effort/Action metric β pass 1 exact slug/alias for compiler-origin,
pass 2 fuzzy with synthetic fallback β and inserts the effort-data metric inline.
Origin provenance: compiler β analyzed β analyzed-estimated
(effortResolution.ts:79-88).
| Surface | Where | Matching | Case | Miss behavior |
|---|---|---|---|---|
WQL find:effort
|
/efforts catalog, CLI |
effort: = slug exact or label/alias lowercase equality; text: = case-insensitive substring over label+slug+aliases; discipline:/intensity:/origin: = exact equality; * wildcard only on effort:/text:
|
slug match is case-sensitive; rest lowercased | empty result β never synthetic, no fuzzy (QueryService.ts:1106-1127) |
| Registry hook | useEffortCatalog({text,origin,discipline}) |
substring over label/alias/slug | insensitive | filtered list |
| Composer autocomplete | loadSuggestions('effort') |
none β returns full registry as {value: slug, label} sorted by slug |
β | β |
| Analytics resolution | EffortResolver |
exact β Levenshtein β€ 2 over label+aliases | normalized (normalizeForFuzzy) |
synthesizes unresolved effort, MET 5.0, isEstimated
|
Fuzzy exists only in the analytics path. WQL never injects a resolver, so
find:effort{text:frann} matches nothing while the analytics pipeline would recover
"fran". The useEffortCatalog hook currently has no page consumer β the catalog
searches via WQL instead (drift #7).
Contract is one method: EffortQueryStore.getAllEfforts() (stores.ts:73-75).
Scan-only β no slug/alias/fuzzy/write methods. Default store returns []
(QueryService.ts:186-188); hosts inject: playground RegistryEffortStore over the
composite registry, CLI bundled/corpus efforts, storybook fixtures.
-
Filter keys (exact allowlist,
vocabulary.ts:50-52):effort,discipline,intensity,origin,text.,= AND across keys,|= OR within key,!= negation, trailing*wildcard oneffort:/text:only, quoted values for spaces. -
Pipes:
order by slug|label|registrySource(EFFORT_COLUMNS,language.ts:277),limit.selectis ignored (advisory);windowignored β registry has no time dimension (wql.ts:789-791). -
Result: full
IEffortrecords inFindQueryResult.efforts+stages {selected, matched}. CLI serializeseffort,id,label,slug(formatters.ts:235-257); the app maps toEntry{id: slug, kind:'effort'}(entryMapper.ts:315-341). -
Migration: legacy
?origin=&discipline=&q=URL params rewrite tofind:effort{origin:β¦,discipline:β¦,text:β¦}(useEffortsComposerState.ts:20-49).
effort β WQL_TAG_KEYS (vocabulary.ts:40). On aggregates/notes/blocks it is pure
string matching against fact-row effortSlug: sum:totalVolume{effort:back-squat},
{effort:back*}, {!effort:burpee}, by {effort}, find:note{effort:β¦} /
find:block{effort:β¦} containment via block_efforts.by-effort
(QueryService.ts:897-901,1042-1045). Dotted metric keys <slug>.<family> are legal
and completed from the registry (language.ts:99-104,350-356).
- No fuzzy in WQL (Β§5).
- No time dimension on the registry noun.
-
origin:means registrySource infind:effortbut producer provenance (runtimeβ¦) on aggregates β same key, two value domains. -
synthetic-unresolvedefforts appear in analytics rows but are unlistable in WQL (EFFORT_REGISTRY_ORIGINSexcludes them,types.ts:14-15).
markdown/efforts/*.md ββseedβββΆ IDB efforts store ββhydrateβββΆ CompositeEffortRegistry
β
script line "15 Thrusters" ββparseβββΆ EffortMetric(raw text) ββcompileβββΆ EffortEnrichmentPass
β resolveEffort(label) β effort-data metric (origin compiler)
runtime output statements ββanalyticsβββΆ TwoPassEffortResolution βββΆ calc effort table (MET/factor, default-row)
β
AnalyticsDataPoint.effortSlug βββΆ WQL fact plane {effort:β¦} by {effort}
registry.list() βββΆ EffortQueryStore βββΆ WQL find:effort{β¦} βββΆ /efforts catalog, /e/:slug detail
| Consumer | What it uses |
|---|---|
| Runtime/compiler |
EffortMetric text, effort-data metric, mergeHints
|
| Analytics/calc | resolver, effort lookup table, effort/effortLabel atoms, per-slug grouped calcs |
| WQL |
EffortQueryStore, fact-row effortSlug tags, block_efforts index |
| Playground UI | detail page (create/clone/alias-link/resolved widget), catalog stream, nav discipline tags, composer autocomplete, alias pages |
| Engine CLI | corpus/bundled efforts, find-result IR envelope, CSV/text rows |
| # | Severity | Finding | Evidence |
|---|---|---|---|
| 1 | med | wql IEffort is a loose subset of lang's; three hosts bridge with as unknown as casts |
stores.ts:59-72; engine/cli/query.ts:25,140; queryService.ts:22-23; journals.ts:89-92
|
| 2 | med |
EffortStorageAdapter.delete is dead-broken: storageService.deleteEffort has no implementation anywhere |
IndexedDBEffortStorage.ts:22 is the only reference; no UI delete exists either |
| 3 | low |
EffortDetailPage.tsx:10 claims "IDB-first load with markdown file fallback" β the hook is registry-only |
useEffortContent.ts:66-76 |
| 4 | low |
disciplines.ts:7 comment says 53 bundled efforts; corpus is 160 files |
markdown/efforts/** (verified 2026-10-04) |
| 5 | med | Fresh DB before seed hydration resolves against the 5 test fixtures, not the corpus |
bundled-efforts.ts:1-8 re-exports commonFixtureSet; effortRegistry.ts:23-26
|
| 6 | low |
docs/wql-whiteboard-crosswalk.html:541-547 lists category/plane find:effort filters β never implemented; real allowlist is Β§6.1 |
vocabulary.ts:50-52 |
| 7 | low |
useEffortCatalog substring-filter hook has no page consumer |
EffortRegistryContext.tsx:78-101 |
| 8 | low | Case-sensitivity asymmetry: find:effort{effort:X} slug compare is case-sensitive; label/alias compares are lowercase; discipline:/intensity:/origin: are raw exact |
QueryService.ts:1106-1127 |
| 9 | low | Calc authoring suggests a static effort list, not registry-live | calcCompletion.ts:38,74-76 |
| 10 | info | Only 7 hint keys are consumed; any other hints key on an effort is silently inert |
hints.ts:68-84 |
| 11 | info |
origin: key overload: registrySource (find:effort) vs producer provenance (aggregates) |
Β§6.3 |
| 12 | info | Seed corpus bodies: 158 of 160 efforts are frontmatter-only | docs/prototypes/effort-page-coverage-audit.md:21-23 |
- Entity: types.ts, disciplines.ts, EffortResolver.ts, CompositeEffortRegistry.ts
- Runtime/analytics: EffortMetric.ts, EffortEnrichmentPass.ts, effortResolution.ts, TwoPassEffortResolutionProcess.ts, tables.ts
- WQL: stores.ts, QueryService.ts (
runFindEffort1101-1162), vocabulary.ts, capabilities.ts, language.ts, derivation.ts - App: EffortDetailPage.tsx, useEffortContent.ts, EffortRegistryContext.tsx, effortRegistry.ts, IndexedDBEffortStorage.ts, IndexedDBStorage.ts, effort-markdown.ts, SeedImporter.ts, queryService.ts, staticBlockIndex.ts
- Engine/CLI: cli/query.ts, formatters.ts, ir.ts
- Corpus:
markdown/efforts/<discipline>/<slug>.md - Tests:
packages/lang/tests/effort-registry/,packages/lang/tests/analytics/TwoPassEffortResolutionProcess.test.ts,packages/wql/tests/findEffort.test.ts,packages/wql/tests/QueryService.test.ts,e2e/live-app/effort-detail.e2e.ts