Tier 3 nao walkthrough
This walkthrough treats nao as the Tier 3 analytical surface: a schema-aware
AI-IDE for the analyst who is changing SQL, not a dashboard that the platform
deploys for readers. The session starts from the local analytical contract:
the DuckDB warehouse materialised from the tutorial fixtures and the dbt
project under tutorial/serving/dbt. That is the same substrate used by the
Evidence incumbent and by the future Tier 2 BI surface, but nao enters at a
different moment. The analyst is inside the authoring loop, asking why a query
fails across publishers and deciding whether the fix belongs in staging,
evaluation, or lesson prose.

The first flow is connection and schema discovery. In nao, the analyst opens a
workspace that points at the DuckDB file produced by the serving module and at
the dbt project root. The useful view is not only a table list. It joins dbt
model metadata, column descriptions, tests, and file paths into one navigation
surface. stg_ebnerd_articles shows article_id, title, category,
published_at, sentiment_score, ner_entities, and publisher.
stg_adressa_articles shows the same canonical article identity fields but
no sentiment_score. stg_mind_articles adds subcategory, abstract, and
url, keeps published_at nullable, and also has no sentiment column.
That difference is the point of the lesson. ADR-0006 says the analytical
contract is for people asking direct questions of the warehouse, but a direct
question is not allowed to flatten publisher-specific data into a fake common
shape. The nao lineage panel makes that visible before the analyst writes a
fix. Selecting category shows how EB-NeRD’s single token, Adressa’s
slash-separated path, and MIND’s broader category taxonomy all arrive at a
shared column name through different staging choices. Selecting
sentiment_score shows that only EB-NeRD contributes the column. The absence
is not an error; it is a contract detail downstream code must respect.

The second flow is the copilot suggestion. The analyst asks: “Why does this
cross-publisher sentiment query fail when I include Adressa and MIND, and what
would be the smallest honest fix?” A naive query groups all article staging
models together and calls avg(sentiment_score). That feels natural if the
analyst has only worked with EB-NeRD, but it breaks the moment Adressa enters
because Adressa lacks sentiment, and it misleads for MIND because MIND brings
a different category taxonomy and no article-level sentiment. The right fix is
not to invent neutral scores. The right fix is to make availability explicit.
Nao’s useful suggestion is a small SQL rewrite: keep publisher, keep a
normalised category_family for comparison, and compute sentiment only where
the source actually exposes it. The suggestion reads the dbt schema docs and
the model SQL, so it can explain why sentiment_score should be wrapped as
CASE WHEN publisher = 'ebnerd' THEN sentiment_score END or joined through an
EB-NeRD-only metric CTE rather than coalesced to zero. It also suggests a
separate sentiment_available flag. That flag is what makes the degradation
graceful: dashboard, BI, and evaluation consumers can display “not available”
instead of treating missing measurement as neutral editorial sentiment.
This is where nao is asymmetric to Tier 1 and Tier 2. A static generator can publish the final explanation, and a BI server can let an analyst filter the finished metric. Neither one should be the place where schema drift, dbt model docs, column tests, and proposed SQL edits are negotiated. Nao lives in the editor loop, close to the files and the tests. It helps the analyst change the contract without hiding the contract.

The third flow is the before/after diff. The analyst accepts the suggested
rewrite into a scratch model or a lesson query and nao shows the before/after
diff beside the lineage graph. The before side has one implicit assumption:
every publisher can support avg(sentiment_score). The after side splits
that assumption into two fields, sentiment_available and
mean_sentiment_when_available, and leaves category comparison as a separate
normalisation step. That diff matters because it is reviewable. The reviewer
can see the exact query change instead of reading a chat transcript and
guessing what landed.
The lineage view then propagates downstream impact. A change that only touches
the analytical metric should not imply that article_embeddings or the app
contract has changed. A change that alters staging columns should light up the
models, tests, and docs that read those columns. In this walkthrough, the safe
boundary is clear: the staging contract already documents the absence of
sentiment for Adressa and MIND, so the fix belongs in the analytical query and
lesson framing. Nao helps prove that by showing which downstream surfaces
would change and which would stay untouched.
ADR-0023 is the framing reference for this Tier 3 role. It says nao packages schema, docs, rules, tests, and platform metadata into a conversational analytics surface over the DuckDB/Parquet analytical contract. Module 13 narrows that into a tier distinction. Nao is not Tier 1 because it does not produce durable static pages for readers or stakeholders. It is not Tier 2 because it is not a deployed surface for interactive BI sessions. It is not a deployed surface at all, and it does not serve readers. It serves the analyst or engineer who is authoring, debugging, and testing analytical work before a public surface gets rebuilt.
That limitation is also the reason to include it. The platform needs Tier 1 for permanent reference pages and Tier 2 for governed exploration, but it also needs a place where the analyst can ask schema-aware questions while making edits. The Adressa/MIND sentiment case is the smallest honest example: nao does not make missing columns disappear; it makes the missingness visible early enough that the deployed analytical surfaces can stay truthful.