Doc WREN-ONT-001Issue 1 · 3 Nov 2026Wren OSS 0.14.0 · Trino · dbtWriting standard: ASD-STE100

Wren Ontology Manual for Analysts

This manual tells you how to change the business meaning of TipTip and SatuSatu data in Wren. It explains the terms in plain words. It gives step-by-step tasks to define, check, publish, and restore a change.

00

How to read this manual

This manual uses ASD-STE100 Simplified Technical English. The writing rules make each instruction short and clear. You can read it quickly, also when English is your second language.

ONE ACTION, ONE STEPEach numbered step gives one instruction.
COMMAND FORMSteps start with a verb: "Open", "Run", "Record".
SHORT SENTENCESProcedure sentences have 20 words or fewer.
CONDITION FIRST"If the build fails, do step 5." The condition comes first.
RESULT LINEEach task ends with the result that you must see.
TECHNICAL NAMESFile, table, and tool names stay as they are in the system.

The manual uses three types of safety notice. They come before the step that they apply to.

WARNING

A WARNING tells you about a risk of wrong business numbers or exposure of personal data. Obey all WARNINGs.

CAUTION

A CAUTION tells you about a risk of damage to the service, the files, or the deployment.

NOTE

A NOTE gives you more information to help you do the task.

Who does what

These are working roles. One person can have more than one role.

Analyst

Owns the meaning

Defines terms, edits the Wren files, and compares results with evidence.

Business owner

Approves definitions

Confirms financial definitions, status meanings, and reporting exceptions.

Data engineer

Owns the pipeline

Confirms the deployed dbt code, the source structure, and pipeline health.

Deployment operator

Owns the server

Publishes files, restarts Wren, reads logs, and restores old versions.

01

The system in one picture

Data goes through two layers. The data layer moves and prepares the records. The meaning layer tells an AI what the records mean. You, the analyst, manage the meaning layer.

MEANING LAYER · YOU MANAGE THIS DATA LAYER · DATA ENGINEERING MANAGES THIS Analyst defines meaning wren-project/ models, relationships, rules, SQL examples Compiled MDL target/mdl.json Wren MCP the translator AI client LibreChat, Claude, ChatGPT, Gemini build load at start question → modeled SQL Source apps TipTip, SatuSatu databases dbt pipeline cleans and prepares tables on a schedule Lake tables data_staging data_mart Trino runs the SQL reads physical SQL rows dbt code = reference for definitions
Fig. 1 — Two layers. Blue arrows carry meaning. Grey arrows carry data.
dbt

The kitchen prep

dbt cleans raw ingredients before service. It runs on a schedule, not when you ask a question.

Trino

The engine

Trino runs SQL and reads tables from many storage places with one language.

Wren

The translator

Wren changes business names like product_data into real table locations for Trino.

MCP

The universal plug

MCP lets any AI app use Wren tools with one standard connection.

NOTE

A Wren build does not refresh data. A Wren restart does not correct a dbt calculation. If the source number is wrong, speak to the data engineer.

02

Terms in plain words

Type a word to filter the list. The third column gives an everyday comparison.

TermPlain meaningLike…In this project
OntologyThe agreed map of business things and how they connect.A family tree for data.A buyer places an order for a product.
Semantic layerThe layer that gives data business names and fixed calculations.A restaurant menu over the kitchen.Wren shows product_data, not a long table name.
ContextEverything the AI can read about the data before it writes SQL.A briefing pack for a new staff member.Models, relationships, rules, and SQL examples.
ModelA named set of records that Wren makes available.A labelled drawer in a cabinet.product_data, order_data.
ColumnOne named value in each record.One box on a form.product_uid, title.
Data typeThe kind of value in a column."Number only" or "text only" on a form.BIGINT = whole number. VARCHAR = text.
GrainWhat one row represents."One line per receipt" or "one line per item on the receipt".One row per product is not one row per invoice.
Primary keyA column that identifies exactly one row.A national ID number.product_data.id.
Join keyThe value that connects records in two models.The order number on a delivery slip.content_id → product_data.id.
RelationshipA declared, checked connection between two models.A road between two drawers.Entries in relationships.yml.
CardinalityHow many records can match on each side of a join.Many students, one teacher.MANY_TO_ONE: many refund rows → one product.
JoinA SQL step that combines matching records.Stapling the delivery slip to the order.Add a product title to each transaction.
Fan-outA join that copies rows, so totals become too large.Counting one bill three times.One invoice amount repeated on 3 transaction rows.
DimensionA value that you group or filter by.The labels on a pivot table's rows.Product title, status, month.
MeasureA value that you calculate from many rows.The total at the bottom of a column.Sum of approved amounts.
MetricA measure with an agreed business definition."Revenue" as finance defines it.Needs amount, date basis, exclusions, currency.
AggregationA calculation over many rows.Adding up a shopping list.SUM, COUNT, AVG.
MDLModeling Definition Language. Wren's compiled description of all models.The printed rulebook from many draft pages.target/mdl.json. Never edit it by hand.
MetadataInformation that describes the data.The label on a jar.metadata.yml descriptions.
Reference SQLSQL that builds a model's records from source tables.A recipe for one drawer's contents.ref_sql.sql in a model folder.
ViewA saved query that acts like a table.A saved filter on a spreadsheet.event_refund_export.
Modeled SQLSQL that uses Wren names.Ordering from the menu.SELECT id FROM product_data.
Physical SQLSQL that uses real database locations.Going into the kitchen yourself.catalog.schema.table.
CatalogA named data connection in Trino.A building.tiptip_datalake_prd_s7jc.
SchemaA group of tables inside a catalog.A floor in the building.data_staging, data_mart.
dbt modelCode that prepares one table or view.A prep recipe in the kitchen.content_svc_product_clean.sql.
LineageThe path from source data to an answer.A parcel tracking history.App DB → dbt → Trino table → Wren model → answer.
Business ruleAn instruction about meaning or use, in plain text.A note in the staff handbook.knowledge/rules/*.md.
SQL pairA confirmed question with its approved SQL.A worked example in a textbook.knowledge/sql/*.md.
MCPModel Context Protocol. A standard plug between AI apps and tools.A USB-C port.https://wren.tnexti.net/mcp.
BuildConverts project files into compiled MDL.Spell-check plus print.Proves the structure is valid. Not that numbers are right.
Dry planShows the translated SQL without running it.Reading the route before you drive.MCP tool dry_plan.
Dry runAsks the database to check SQL without returning rows.A test start of the engine.MCP tool dry_run.
ReconciliationComparing your result with a trusted result.Matching a bank statement to a ledger.Same period, same counts, same totals.
FreshnessHow recently the data arrived.The "baked on" date on bread.A timestamp alone does not prove the pipeline finished.
PIIPersonal data that can identify a person.Anything on an ID card.Names, emails, phone and ID numbers.
WIBWestern Indonesia Time, UTC+7.Jakarta clock time.Some dbt timestamps are already WIB.
CAUTION

The words catalog and schema have two uses. In wren_project.yml they name the Wren namespace (data_mart). In a model's table_reference they name the real table location. These can be different. product_data reads from data_staging.

03

The current ontology

The project tiptip_poc has 20 models, 3 views, 8 relationships, 5 rule files, and 6 SQL examples. Fig. 2 shows the refund cluster. All its relationships are many-to-one.

buyer_id = id creator_id = id content_id = id guest_order_id invoice_id invoice_id ticket_schedule_id ticket_option_id event_refund_transactions many rows per invoice user_dataregistered buyer · PII creator_dataorganizer profile product_dataone row per product guest_order_dataguest checkout · PII payment_dataone record per invoice product_booking_datalowest booking per invoice ticket_schedule_dataschedule → option ticket_option_datapresale, regular, VIP
Fig. 2 — Refund cluster in relationships.yml. Arrow points to the "one" side.
DomainObjectsRules file
Refunds and event cancellationevent_refund_transactions, event_ticket_holder_data, view event_refund_exportrefund_model.md
Orders and bookingsorder_data, product_booking_data, payment_dataorder_booking_lifecycle.md
Vouchers and coinswallet_coin_ledger_datavoucher_usage.md
War Room — Ticketingwar_room_revenue, war_room_operations, war_room_source_checks, war_room_creator_agency_history, war_room_report_reference, views war_room_report_components, war_room_reconciliationwar_room_ticketing.md
War Room — SatuSatuwar_room_satusatu_base, _components, _operationswar_room_satusatu.md
04

Which file to change

Each type of change has one correct place. Use Fig. 3 to find it.

flowchart TD
  Q{"What do you need to change?"}
  Q -->|"Meaning of a column"| M1["models/{name}/metadata.yml
column description"] Q -->|"Show a new source column"| M2["metadata.yml columns list
+ ref_sql.sql if the model has one"] Q -->|"Connect two models"| R["relationships.yml"] Q -->|"Reusable calculation"| C{"Is it shared by other
dashboards and pipelines?"} C -->|"Yes"| D["dbt project
(with the data engineer)"] C -->|"No, Wren only"| V["ref_sql.sql or views/{name}/"] Q -->|"Date, status, currency convention"| K["knowledge/rules/{topic}.md"] Q -->|"A confirmed worked example"| S["knowledge/sql/{question}.md"] Q -->|"Evidence or test SQL"| E["queries/ or docs/"]
Fig. 3 — Decision tree for the file that owns the change.
tree
wren-project/
├── wren_project.yml            Project settings (namespace, data source)
├── models/<model_name>/
│   ├── metadata.yml            Source location, columns, descriptions, key
│   └── ref_sql.sql             Source query (only some models)
├── relationships.yml           Connections between models
├── views/<view_name>/metadata.yml   Saved query over Wren models
├── knowledge/
│   ├── rules/<topic>.md        Business instructions in plain text
│   └── sql/<question>.md       Confirmed question + modeled SQL
├── tests/                      Synthetic SQL behaviour checks
└── target/mdl.json             Generated. Do not edit.
WARNING

A description or a rule does not force a calculation. The AI can ignore text. Put required calculations in executable SQL. Use the rule to explain the meaning and the exceptions.

Removing a column description does not remove access to the column. Database permissions still control access.

05

Change lifecycle

Every change follows the same route. Tasks A to J below follow this order.

flowchart LR
  A["A · Define
expected result"] --> B["B · Trace source
dbt + Trino"] B --> E["C–G · Edit
Wren files"] E --> H{"H · Validate
in staging"} H -->|"Fail"| E H -->|"Pass"| RV["Review
+ approve"] RV --> I{"I · Publish
+ restart"} I -->|"Build or answer fails"| J["J · Restore
previous version"] J --> I I -->|"Pass"| REC["Record version
+ evidence"]
Fig. 4 — From a question to a released ontology change.
TASK A

Define the expected result

Why
You cannot check a change if you do not know the correct answer first.
Who
Analyst, with the business owner
Needs
A trusted report or checked SQL result
  1. Write the business question in one sentence.
  2. Name the person who owns the definition.
  3. Write what one output row represents (the grain).
  4. List the output columns.
  5. List the filters and the exclusions.
  6. Write the date column and the time zone (UTC or WIB).
  7. Write the currency and the rounding rule.
  8. Record the expected row counts and totals.
  9. Record the source and the date of the trusted result.
change record
Business question:
Definition owner:
One output row represents:
Required columns:
Filters and exclusions:
Date column and time zone:
Currency and rounding:
Trusted comparison and date:
Expected counts and totals:
Known exceptions:
Affected models, views, rules, and SQL pairs:
dbt environment and deployed revision:
Reviewer:
RESULTAnother analyst can find the same records and calculate the same answer.
TASK B

Trace a source in dbt and Trino

Why
You must know where each column comes from before you change its meaning.
Who
Analyst, with the data engineer
Needs
dbt repository (../data-transformation), read access to Trino
  1. Open the model file models/<name>/metadata.yml.
  2. If the model has ref_sql.sql, open that file also.
  3. Record each physical table that the model reads.
  4. Find the dbt model that builds each table.
  5. Read its SQL, configuration, macros, and column descriptions.
  6. Read the dbt tests for uniqueness and nulls.
  7. Ask the data engineer to confirm the deployed environment and revision.
  8. Run DESCRIBE on the table in Trino.
  9. Compare the Trino columns with the Wren column list.
  10. Record each difference before you change a definition.
NOTE

dbt SQL contains {{ ref(...) }} and {{ source(...) }}. These are dbt instructions, not Trino SQL. Use the compiled SQL from the deployment to see the real query.

A dbt unique_key setting does not prove that the data is unique. Check the test results and the data.

Example: trace product_data

Wren modelwren-project/models/product_data/metadata.yml
Trino tabletiptip_datalake_prd_s7jc.data_staging.content_svc_product_clean
dbt candidateproduction-tencent/models/data_staging/contents_svc_clean/content_svc_product_clean.sql
Exposed nowid, product_uid, title
Trino SQL
DESCRIBE tiptip_datalake_prd_s7jc.data_staging.content_svc_product_clean;

-- Check a proposed primary key
SELECT
    COUNT(*)           AS row_count,
    COUNT(id)          AS nonnull_id_count,
    COUNT(DISTINCT id) AS distinct_id_count
FROM tiptip_datalake_prd_s7jc.data_staging.content_svc_product_clean;
RESULTAll three counts are equal and larger than zero. You can explain where each column comes from.
CAUTION

For a large table, agree the scan scope with the data owner first. A sample does not prove uniqueness outside that sample.

TASK C

Add a column to a model

Example
Add product publication status status_name to product_data. This is a proposed change.
Before
Complete Task A and Task B.
  1. Make sure that Trino shows status_name as a text column.
  2. Open wren-project/models/product_data/metadata.yml.
  3. Add the item below inside the existing columns list.
  4. Keep the existing columns and primary_key: id.
  5. Write a description that tells what the column is not, if confusion is possible.
  6. Do Task F to add the matching rule.
yaml
  - name: status_name
    type: VARCHAR
    properties:
      description: >-
        Product publication status from the cleaned dbt product model.
        This is not an order, booking, or refund status.
RESULTAfter publication, describe_model shows status_name. A query returns the same values as Trino.

Make a new model only if necessary

First look in models/ for a model with the same source and grain. Extend that model if it exists. A new table model has this structure:

yaml
name: <model_name>
table_reference:
  catalog: tiptip_datalake_prd_s7jc
  schema: data_staging
  table: <physical_table>
properties:
  description: One row per <thing>. Use <key> to connect <other model>.
columns:
  - name: id
    type: BIGINT
    properties:
      description: Internal identifier.
primary_key: id
WARNING

Show only the columns that the approved analysis needs. Do not add PII columns without approval.

CAUTION

Use spaces, not tabs, in YAML. Use the real source data type. A model uses table_reference or reference SQL, not both.

TASK D

Add or check a relationship

Why
A wrong relationship copies rows and makes totals too large.
join_typeMeaning, from first model to second model
ONE_TO_ONEEach key matches at most one row on each side.
MANY_TO_ONEMany first-model rows match one second-model row.
ONE_TO_MANYOne first-model row matches many second-model rows.
MANY_TO_MANYMany rows on both sides share the key. Use with great care.

See the fan-out problem

order (1 row)
invoiceamount
INV-001300,000
transactions (3 rows)
invoiceticket
INV-001T1
INV-001T2
INV-001T3
after join
invoiceticketamount
INV-001T1300,000
INV-001T2300,000
INV-001T3300,000
SUM(amount)900,000 ✗
Correct300,000 ✓
Fig. 5 — One invoice amount joined to three transaction rows is counted three times.
WARNING

Do not use DISTINCT to hide a fan-out. Correct the join, or calculate at the correct grain before the join.

Procedure

  1. Write the grain of both models.
  2. Count null keys on both sides.
  3. Check the "one" side for duplicate keys. Use the query below.
  4. Count the rows before the join and after the join.
  5. Compare the money totals before the join and after the join.
  6. Count the rows that have no match.
  7. If all checks pass, add the relationship to relationships.yml.
Modeled SQL via Wren
-- Step 3: must return no rows
SELECT id, COUNT(*) AS key_rows
FROM product_data
GROUP BY id
HAVING COUNT(*) > 1
LIMIT 20;

-- Step 4: for MANY_TO_ONE + LEFT JOIN, both counts must be equal
SELECT COUNT(*) AS source_rows FROM event_refund_transactions;

SELECT COUNT(*) AS joined_rows
FROM event_refund_transactions t
LEFT JOIN product_data p ON t.content_id = p.id;
relationships.yml (existing entry)
  - name: event_refund_transactions_product
    models:
      - event_refund_transactions
      - product_data
    join_type: MANY_TO_ONE
    condition: event_refund_transactions.content_id = product_data.id
RESULTRow counts and totals stay the same after the join. Unmatched rows are known and explained.
NOTE

Apply the same date or event filter to both count queries. A relationship does not add a join to a view automatically. Write the join in the view SQL.

TASK E

Add a calculation or a view

Rule
Shared data preparation goes in dbt. Wren-only meaning and reusable query shapes go in Wren.
Put it inWhenExample
dbtOther dashboards or pipelines need the same logic.Cleaning product status codes.
ref_sql.sqlA direct table map cannot make the records you need.payment_data: one metadata record per invoice.
Wren viewA saved query over existing Wren models.event_refund_export.

Change model SQL

  1. Open the model's ref_sql.sql.
  2. Check each source table and each join.
  3. Add the new expression to the SELECT list.
  4. Add the output column to metadata.yml.
  5. Check the grain and the totals again.

Add a view

  1. Make sure that no existing object answers the question.
  2. Make the folder wren-project/views/<view_name>/.
  3. Make the file metadata.yml in that folder.
  4. Write the grain in the description.
  5. Write the SQL with Wren model names only.
views/published_product_directory/metadata.yml
name: published_product_directory
properties:
  description: One row per product with the publication status Published.
statement: |
  SELECT id, product_uid, title
  FROM product_data
  WHERE status_name = 'Published'
WARNING

For a financial metric, record the formula, date basis, grain, currency, exclusions, and rounding. Do not change VAT formulas or finance adjustments without owner approval.

NOTE

A view does not make a Trino table. It does not refresh data. Check the exact status spelling in Trino before you filter on it.

TASK F

Add a business rule

Why
Rules tell the AI about meanings that the schema cannot show.
  1. Make the file wren-project/knowledge/rules/<topic>.md.
  2. Write one statement per line. Use short sentences.
  3. Write what the term means and what it does not mean.
  4. Verify each statement against data or the owner.
  5. Record the owner and the source in the change record.
knowledge/rules/product_publication.md
## Product publication

Use product_data to resolve product identifiers.
Use id to connect product_data with transaction content_id.
Use product_uid to resolve an external event detail identifier.
Titles can repeat.

status_name describes product publication.
It does not describe payment, order, booking, or refund status.
Published status alone does not prove current ticket availability.
RESULTAfter publication, get_instructions returns the rule.
NOTE

Rules guide the AI. They do not force it. Configure each AI client to call get_instructions before it queries.

TASK G

Save a confirmed SQL example

Why
The AI finds similar examples with recall_queries and copies their pattern.
Before
The query passes Task H against the trusted result.
  1. Make the file wren-project/knowledge/sql/<question>.md.
  2. Write the question in nl as a user would ask it.
  3. Write the approved modeled SQL in sql.
  4. Add a note about the limits of the example.
  5. Remove customer data, IDs from real cases, and credentials.
  6. If a definition changes, update all related examples in the same change.
knowledge/sql/published-products.md
---
nl: "List product identifiers and titles for products with publication status Published."
sql: |
  SELECT id, product_uid, title
  FROM product_data
  WHERE status_name = 'Published'
  ORDER BY id
source: user
datasource: trino
tags:
  - products
  - publication
---

This query uses product publication status.
It does not check ticket availability or payment status.
Check result limits before treating the returned list as complete.
RESULTAfter publication, recall_queries can return the example for a similar question.
NOTE

MCP store_query is disabled in this deployment. Save examples only through reviewed files in Git. Recall can return a different example or none. Keep essential instructions in the rule file.

TASK H

Validate a candidate

Where
A separate checkout or a staging deployment
Image
wrenai==0.14.0, wren-core-py==0.7.6
CAUTION

The production container reads ./wren-project directly. Do not edit files there to test. Use a separate checkout.

  1. In the candidate checkout, build to a temporary file.
  2. Fix each build error. Build again.
  3. Load the candidate into a separate staging Wren MCP service.
  4. Start a new chat with the staging service.
  5. Call get_instructions. Look for the new rule.
  6. Call describe_model for each changed model.
  7. Call recall_queries with the test question.
  8. Call dry_plan. Read the translated tables, joins, and filters.
  9. Call dry_run for the proposed SQL.
  10. Call run_sql for the approved read-only checks.
  11. Compare the results with the change record from Task A.
  12. If you changed War Room SQL, run the synthetic tests.
bash
# Step 1 — build without replacing the production manifest
docker compose run --rm --no-deps --entrypoint wren wren-mcp \
  context build --path /project --output /tmp/wren-validation-mdl.json

# Step 12 — synthetic checks (staging)
docker compose exec -T wren-mcp python /project/tests/test_war_room.py
docker compose exec -T wren-mcp python /project/tests/test_war_room_satusatu.py

Acceptance checks

CheckEvidence you must have
Source mappingCorrect deployed table. Compatible data types.
Primary keyNo duplicates. No nulls in scope.
RelationshipCardinality, unmatched rows, row counts, and totals checked.
MeaningStatuses, date basis, time zone, currency, rounding are correct.
ResultCounts and totals agree with the trusted result.
Edge casesEmpty results, nulls, refunds, and date boundaries behave as defined.
ContextMCP shows the new structure and rules.
ExamplesRelated SQL pairs still use valid objects.
OutputNo hidden truncation. No unintended PII.
WARNING

A successful build proves the structure only. A successful dry run proves the SQL is valid only. Neither proves that the business number is correct. Only reconciliation proves that.

RESULTAll acceptance checks pass. The evidence is in the change record.
TASK I

Publish the change

Who
Deployment operator
Needs
Reviewed files, recorded checks, approved release window, a recoverable previous version
  1. Record the current deployed Git revision.
  2. Publish the reviewed source files to the deployment checkout.
  3. Restart the Wren MCP service.
  4. Read the startup log.
  5. Make sure that the log shows "Built:" before "Starting Wren MCP Server".
  6. Start a new chat with the deployed MCP service.
  7. Do steps 5 to 11 of Task H again.
  8. Record the release revision, the check time, and the result.
bash
docker compose restart wren-mcp
docker compose logs --since=5m wren-mcp
expected log
==> Building Wren context...
Built: <model-count> models, <view-count> views → /project/target/mdl.json
==> Starting Wren MCP Server on port 8080...
WARNING

If the log shows Context build initial attempt failed, the release failed. The server can still start with the old context. A running container does not prove that the new ontology is active.

NOTE

Wren does not reload files automatically. Old chats can keep old tool results. Always test in a new chat. The public endpoint is https://wren.tnexti.net/mcp. Use the approved client sign-in.

RESULTThe new chat returns the new context and the expected answers.
TASK J

Restore the previous version

When
The build fails, or the new definition gives wrong results.
  1. If reports use the wrong definition, stop those reports.
  2. Find the previous approved Git revision.
  3. Restore the models, views, relationships, rules, and SQL pairs together.
  4. Restart Wren with the commands in Task I.
  5. Make sure that the build succeeds in the log.
  6. Ask the previous acceptance questions in a new chat.
  7. Record the failure and the restored revision.
CAUTION

Do not restore only target/mdl.json. The source files and the compiled file must agree. A Wren restore does not undo a dbt data change.

RESULTThe previous answers return in a new chat.
06

Troubleshooting

SymptomCheckAction
New column is not in MCPBuild log, loaded MDL, active server.Fix the build error. Restart. Test in a new chat.
Trino cannot find a columnPhysical table structure and model SQL.Correct the mapping, or request the upstream change.
Total increased after a joinDuplicate keys and grain on both sides.Correct the relationship, or aggregate at the correct grain first.
SQL runs but the answer is wrongDate basis, exclusions, status source, formula.Correct the SQL or the rule. Reconcile again.
AI ignores a ruleResult of get_instructions and client instructions.Make the client fetch rules. Put stable logic in SQL.
Recall returns an old exampleFiles in knowledge/sql/, memory index.Correct or remove old pairs. Rebuild the index if used.
dbt code disagrees with TrinoDeployed revision, target, compiled SQL.Solve the difference with the data engineer first.
Dates are 7 hours wrongUpstream conversion and timestamp type.Confirm UTC or WIB before you convert again.
A list looks incompleteTool row limits, row counts, export.Confirm completeness before you use it as evidence.
Server starts after a failed buildEntrypoint warning, old manifest.Treat the release as failed. Fix or restore (Task J).
07

Project facts to keep

These meanings are already verified. Do not change them without the owner.

Products

product_uid is the external ID. Refund rows use internal content_id. Resolve through product_data.id first. Titles can repeat.

Orders

order_data has one row per order ID. Its invoice ID is not a verified unique key.

Refund rows

event_refund_transactions can have many rows per invoice. Check the export grain.

Bookings

product_booking_data takes the lowest booking ID per invoice.

Payments

payment_data picks metadata deterministically. It does not rank success or recency.

Statuses

Order status, booking payment status, and transaction flags describe different records.

Refund dates

A source refund date does not prove that the payment provider settled the refund.

Time zone

Some dbt staging timestamps are already WIB (UTC+7). Check before you convert.

War Room

SQL revenue inputs are not final Net Revenue. Report references are dated snapshots, not live data.

08

Review checklist

Tick each item before you ask for publication. Your ticks stay in this browser only.

09

References

In the repository

docs/analyst_ontology_guide.mdMarkdown source for this manual
docs/wren_context_workflow.mdTechnical context workflow
docs/data_transformation_wren_review_20261023.mddbt review and source mappings (dated evidence)
docs/refund_domain_mcp.mdRefund domain and accepted limits
docs/war_room_ticketing.mdTicketing models and validation
docs/satusatu_model_improvement_proposal.mdSatuSatu models and validation
queries/sample-query-using-product-uid.sqlProduct identifier example

Official documentation

NOTE

Online documentation can describe newer features. For commands and syntax, use the pinned versions in this repository first.