wow-view-definition ยท diff
git:20260926.cef4105 to git:20261001.a02cf76
29 added, 27 removed. Audit A to A.
---
name: "wow-view-definition"
- description: "Write or revise Wow View Engine view definitions (record, analysis, dashboard), their system views and their Storybook stories from a business scenario and the service's query capability descriptor. The definition only narrows what the descriptor grants, uses business wording for labels, keeps sensitive and deprecated fields out of analysis, and is proved by the engine's admission. Use for @ahoo-wang/wow-view-engine definitions in downstream apps and for the view definitions and stories inside the Wow repository. Exclude runtime client code (wow-client), answering data questions (wow-data-query), and changing the view engine itself."
+ description: "Decide what a Wow View Engine view definition declares for its audience, and write it with defineView over the service's committed query capability descriptor: which fields appear and in what words, what is narrowed, the time a board reads, system record and analysis views, dashboards, event streams, and the words behind every text(key). Self-checked by the engine's admit. Use for @ahoo-wang/wow-view-engine definitions, system views and boards in downstream apps, and for the view definitions and stories inside the Wow repository. Exclude registering resources, ViewHost, routes, stores and declared actions (wow-view-host), runtime client code (wow-client), answering data questions (wow-data-query), and changing the view engine itself."
---
# wow-view-definition
- The deliverable is code a reviewer can merge: a `ViewDefinition` (fields, record and analysis capability, system views) or a dashboard, the descriptor it was written against, and the tests or stories that show the engine admits it. The descriptor says what the service can do; the definition chooses what this audience sees. **It only narrows: it never declares an operator, sort, group, metric, search, paging mode or limit the descriptor does not grant.**
+ The deliverable is code a reviewer can merge, per dataset: a `defineView(descriptor, spec)` module, the descriptor snapshot it reads (committed beside it), the words for each of its keys in every language the host serves, its system views and boards, and a host test in which `admit` reports nothing the definition did not decide on.
- ## Before writing
+ The descriptor holds the **facts**: paths, kinds, values, sensitivity, deprecation, element structure, and what the store can filter, sort and aggregate. `defineView` takes them from the snapshot, and the runtime narrows to the live descriptor. The definition holds only **choices**: for whom, which fields, in what words, what is narrowed, what a reader opens on. This Skill is about those choices; the engine checks the rest. Registering the definition, its source, routes, store and commands is `wow-view-host`.
- 1. **Get the descriptor, in this order:** a descriptor file already committed beside the definitions (a fixture), then a development or staging service: `GET {base}/{aggregate}/snapshot/schema` for snapshots, `GET {base}/{aggregate}/event/schema` for event streams. Reading a production service needs the user's explicit consent in this conversation; a production URL in config, docs or history is not consent. Credentials come from the environment; never ask for a token in chat or print one.
- 2. **Commit what you read.** Save the descriptor JSON next to the definitions (the repository's fixture convention) and name its `version` in the definition's comment or test, so a later descriptor shows drift in review.
- 3. **Read the scenario for the audience:** who opens the view, what they work through (record), what they ask (analysis), what they glance at (dashboard), and what one record is called for them (`recordNoun`).
- 4. Load `references/definition-anatomy.md` before the first field.
+ ## Before writing
- ## Workflow
+ 1. **Get the descriptor, in this order:** a descriptor already committed beside the definitions; then a development or staging service: `GET {base}/{aggregate}/snapshot/schema` for snapshots, `GET {base}/{aggregate}/event/schema` for event streams. Reading a production service needs the user's explicit consent in this conversation; a production URL in config, docs or history is not consent. Credentials come from the environment; never ask for a token in chat or print one.
+ 2. **Commit what you read** as JSON beside the definition and name its `version` in a comment or the test, so a later descriptor shows its drift in review.
+ 3. **Read the scenario for the audience:** who opens the view, what they work through (record views), what they ask (analysis views), what they glance at (a board), what one record is called for them (`recordNoun`), and which languages the host serves.
+ 4. Load `references/choices.md` before the first field.
- 1. **Plan in business terms.** List the record views (queues people work), the analysis questions (measure by dimension over a period) and any dashboard. Drop what the scenario does not ask for; a definition is a choice, not a copy of the descriptor.
- 2. **Map every field to the descriptor.** Each `FieldDefinition.name` is a descriptor `path`; write the canonical path, not an alias. An element field is declared inside its array field's `elements`, named relative to the element, and exists in the descriptor with `scope` equal to that array (or in a `variants` entry). A field neither the descriptor nor its fixture lists does not exist: never invent or guess one. Say what is missing and where it would come from (a read-model field, a model declaration), instead of writing it.
- 3. **Declare only a subset.** Per field: `operators` within `filter.operators`; `sortable` only where `sort.paged` (paged) or `sort.cursor` (cursor) is true; analysis `groups` and `functions` within `aggregate`; `distinctCount`, `percentile`, `any` only when the field grants them and `analysis.metrics` lists the metric type; `dateUnits` within `analysis.dateUnits`. Per definition: a `paging` mode the descriptor's `record.paging` lists, a `rowKey` the descriptor sorts, a `search` field only when `record.search` exists with its `searchFields` and `searchMode` inside it, and `maxWindow` or `limits.maxLimit` only to go lower than the descriptor's limits. Never restate a server limit as a definition limit.
- 4. **Keep protected fields out of analysis.** A field with `sensitivity` is never an analysis group, a metric input, an expression operand or a metric filter, and never a `rowKey` or card title. A CONFIDENTIAL field (`comparable: false`) takes no operators, is not sortable and is not a search field. Show a masked value only when the audience needs it; otherwise leave the field out. Never add a derived field or condition that would reveal what the mask hides.
- 5. **Keep event conditions inside the event.** On an event stream, `body` is an array of events: declare it `kind: 'elementMatch'` with `elementTitle: 'bodyType'` and the event fields inside `elements`. A condition on an event's payload (a system view's filter, a metric's filter) is an `ELEMENT_MATCH` on `body` whose predicate holds both the `bodyType` condition and the payload condition, so both apply to the same event. Never write a root-level condition on a `body.*` path.
- 6. **Label for the audience.** Every `title`, `label`, `recordNoun`, option label, field group, system view title and analysis display name is in the audience's business words, in each language the host serves. Never a path, a class or enum constant, or a storage term. Analysis wording follows the engine's vocabulary; see the reference.
- 7. **Avoid deprecated fields.** A field with `deprecated` is replaced by the one its message names. When the scenario truly needs it, keep it, say why in a code comment, and flag it in the report.
- 8. **Match what the descriptor says about the value:** `temporal` against `semantic` (`TEMPORAL_EPOCH` with its `timeUnit`, `TEMPORAL_DATE`), enum `options` against `enum[].value`, and constraints such as `CURSOR_UNIQUE_SORT` (a cursor `rowKey` equals the appended identity), `COUNT_REQUIRES_FILTER`, `PARALLEL_ARRAY_SORT` and `NULL_OR_EMPTY_AS_MISSING`.
- 9. **Write system views and stories** from `references/recipes.md`, then run the self-check below.
+ ## The choices
- ## Self-check
+ 1. **Plan in the audience's terms.** The queues people work, the questions they ask (a measure by a dimension over a period), the board they glance at. Drop what the scenario does not ask for: a definition is a selection, not a copy of the descriptor.
+ 2. **List only what this audience reads, filters or groups by**, in the order they read it. A path you do not list does not appear. The record's identity, or the `record.rowKey` you choose, must be among the listed fields. A field the descriptor does not list does not exist: never invent one; say it is missing and where it would come from (a read-model field, a model declaration).
+ 3. **Words are keys, said at the leaf.** Every title, label, option, field group, system view and metric display name is `text(key)`, with the words in a table per language. Words are the audience's, one word per meaning; never a path, a constant, a class name or a storage word. Word every category value the audience can see, or hide it (`false`): a value you leave out shows the descriptor's description, or the raw constant when there is none, and nothing reports it.
+ 4. **Narrow for the audience, never to restate the store.** Leave capabilities open by default: the source grants them at run time, so one definition deploys to MongoDB and Elasticsearch alike. Narrow where the audience would be misled: `analysis: false` for identifiers and free text, `analysis: { groups: [] }` for an id you count but never group by, `sortable: false` and `operators: []` for text that is only read, `summary` only where a total means something, `dateUnits` the audience thinks in.
+ 5. **Protected and deprecated fields.** The engine keeps a sensitive field out of analysis and a confidential one out of every comparison. Your choice is whether the audience sees it at all: list it only when they need the masked value, and never add a field or condition that reveals what the mask hides. A deprecated path is replaced by the one its message names; keep it only with `deprecated: { message }` saying why, and flag it in the report.
+ 6. **Time.** `timeField` is the moment a board's one date filter means for this dataset (when it was paid, not when it was last touched); a system view overrides it, `null` to be read whole.
+ 7. **Records.** Layouts the audience scans (`table`, `card` with the field that names a card), `rowKey` when the identity is not what people look a record up by, and `rowFields` for any field the host's declared actions read beyond the visible columns, including the aggregate id their commands address when `rowKey` is a business key.
+ 8. **Event streams.** One record is one command's appended events: list `body` with `elementTitle: 'bodyType'`, the event types worded as business events, and payload fields from the descriptor's variants inside `elements`. A condition on an event's payload is one `ELEMENT_MATCH` on `body` holding both its `bodyType` and the payload condition.
+ 9. **System views, analyses and boards** are the starting points, not every possible view: see `references/views-and-boards.md`.
- A definition is done when the engine admits it against the committed descriptor. Run, and report each result:
+ ## Self-check: admit
- - **Admission:** `validateDefinition(definition, kinds)` returns no `error` issue.
- - **Narrowing:** an engine whose source `describe` answers the committed descriptor opens every system view (and every dashboard) with no `error` issue on `onIssue`, and with no `capability.*` warning the definition could have avoided: each such warning is a declared capability the descriptor lacks, so remove it rather than accept it.
- - **Tests and stories:** the host's existing test for its definitions passes (or one is added in its style); stories type-check, lint and pass their interaction tests.
+ A definition is done when the engine admits it. The host's test calls `admit` from `@ahoo-wang/wow-view-engine/testing` over everything it registers, with the committed descriptors by `source` and the words of each language, and expects `[]`; a finding kept on purpose is asserted exactly, with a comment saying why. Run it and fix each finding at the choice it names (`references/admit.md`).
- `references/checklist.md` lists every gate and the report.
+ `admit` already checks every path, kind, value, capability, key, element, system view config and board reference against the descriptors; do not re-verify those by hand or with your own scripts. What it cannot judge is yours: the audience, the words, what is left out, the defaults. Review those against `references/admit.md` before reporting.
## References
- - `references/definition-anatomy.md`: the definition's shape, how each member maps to the descriptor, admission and narrowing codes, and the analysis vocabulary. Load it before writing.
- - `references/recipes.md`: record queue, analysis breakdown and trend, event stream by event type, dashboard, a story, and revising after descriptor drift. Load the recipe for the scenario.
- - `references/checklist.md`: the pre-review checklist, the gates and the final report. Load it before reporting.
+ Links in the references point at `main` on GitHub; in a Wow checkout the same file is at the path after `blob/main/` (for example `typescript/wow-view-engine/README.md`).
+
+ - `references/choices.md`: a `defineView` worked through choice by choice, words and keys, the analysis vocabulary, narrowing, protected, deprecated and event-stream fields. Load it before writing.
+ - `references/views-and-boards.md`: system record and analysis views, derived metrics, boards and their time filter, where definitions live, and revising after descriptor drift. Load it for the views.
+ - `references/admit.md`: the admission test, what each finding asks you to revisit, the review of what `admit` cannot judge, and the report. Load it before the self-check.
+
## Related Skills
- - $wow-client: TypeScript code that sends commands or queries, including wiring the descriptor client into a view source.
+ - $wow-view-host: register the definition with its source and store, `ViewHost`, routes, and the commands on its records.
+ - $wow-client: TypeScript code that sends commands or queries outside the engine.
- $wow-data-query: answer a business question from a running service's data instead of writing a view.
- $wow-debug: a query the descriptor admits but the service rejects, or a result that contradicts the data.