@oxy-hq/cli 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +312 -31
- package/dist/main.mjs +11236 -2822
- package/dist/main.mjs.map +1 -1
- package/dist/{mcp-xxWL3_dz.mjs → mcp-B2KBWNOU.mjs} +86 -3
- package/dist/mcp-B2KBWNOU.mjs.map +1 -0
- package/package.json +6 -5
- package/skills/oxy-agents/SKILL.md +318 -0
- package/skills/oxy-agents/references/quick-reference.md +222 -0
- package/skills/oxy-agents/templates/agentic-template.yml +141 -0
- package/skills/oxy-airway-pipelines/SKILL.md +223 -316
- package/skills/oxy-automations/SKILL.md +61 -15
- package/skills/oxy-cli/SKILL.md +40 -6
- package/skills/oxy-custom-apps/SKILL.md +380 -294
- package/skills/oxy-custom-apps/references/minimal-starter.md +71 -0
- package/skills/oxy-custom-apps/references/performance-and-usage.md +151 -0
- package/skills/oxy-custom-apps/references/reading-data.md +106 -0
- package/skills/oxy-custom-apps/references/sandbox-agent-loop.md +307 -0
- package/skills/oxy-custom-apps/references/verifying-in-the-browser.md +65 -0
- package/skills/oxy-data-apps/SKILL.md +362 -0
- package/skills/oxy-data-apps/references/examples.md +263 -0
- package/skills/oxy-data-apps/references/quick-reference.md +171 -0
- package/skills/oxy-data-apps/templates/dashboard-with-controls.app.yml +128 -0
- package/skills/oxy-data-apps/templates/simple-dashboard.app.yml +102 -0
- package/skills/oxy-data-architecture/SKILL.md +137 -152
- package/skills/oxy-evals/SKILL.md +362 -0
- package/skills/oxy-evals/references/diagnosis.md +243 -0
- package/skills/oxy-evals/references/results-json.md +145 -0
- package/skills/oxy-evals/references/writing-expected.md +112 -0
- package/skills/oxy-semantic-modeling/SKILL.md +239 -59
- package/skills/oxy-semantic-modeling/references/warehouse-casts.md +57 -0
- package/skills/oxy-semantic-modeling/templates/topic-template.yml +38 -0
- package/skills/oxy-semantic-modeling/templates/view-template.yml +119 -0
- package/template/.claude/hooks/oxyc-cloud-session.sh +66 -0
- package/template/.claude/settings.json +16 -0
- package/template/.github/workflows/publish.yaml +15 -13
- package/template/.nvmrc +1 -1
- package/template/.oxyc-managed +18 -5
- package/template/README.md +21 -10
- package/template/package.json +3 -2
- package/template/pnpm-workspace.yaml +28 -17
- package/dist/mcp-xxWL3_dz.mjs.map +0 -1
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oxy-data-architecture
|
|
3
|
-
description:
|
|
3
|
+
description: >-
|
|
4
|
+
Use when deciding where an aggregate gets materialised — writing an `execute_sql` rollup,
|
|
5
|
+
chaining one after an `airway` ingest step, choosing rollup vs semantic `pre_aggregations`, or
|
|
6
|
+
whether raw rows must be retained — and on the symptoms: a dashboard that aggregates at query
|
|
7
|
+
time, a load that wrote every row yet failed, preagg that cannot bucket to 15 minutes or join
|
|
8
|
+
two views, or no "bronze"/"silver"/"gold" in the schema. Pipeline internals:
|
|
9
|
+
`oxy-airway-pipelines`; view syntax: `oxy-semantic-modeling`.
|
|
4
10
|
---
|
|
5
11
|
|
|
6
12
|
# Oxy data architecture: where results get materialised
|
|
@@ -11,97 +17,85 @@ query to tune — it is a rollup that was never built. So the work that produces
|
|
|
11
17
|
those tables has to be systematic: one place it runs, one guarantee about when it
|
|
12
18
|
may read, one rule for what gets kept.
|
|
13
19
|
|
|
14
|
-
That spans Airway ingest, the semantic
|
|
20
|
+
That spans Airway ingest, the semantic model, and scheduled automations, which is
|
|
15
21
|
why it is its own skill rather than a section in one of them — and it does not
|
|
16
22
|
restate them. Fold mechanics, duplicate rows and the pipeline lease are
|
|
17
|
-
**`oxy-airway-pipelines`**; views and
|
|
18
|
-
syntax and schedules **`oxy-automations`**.
|
|
19
|
-
|
|
20
|
-
## The
|
|
21
|
-
|
|
22
|
-
The
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
actively maintained there, and a duplicate rots and then argues with the original.
|
|
28
|
-
|
|
29
|
-
**Its status is `design`, with an open "Decisions needed" section.** So this
|
|
30
|
-
skill splits it: **current state** is taught below as fact, re-verified against
|
|
31
|
-
source and cited; the **target tiering, gap list, and open decisions** are named
|
|
32
|
-
at the end and deliberately *not* taught. Building against a convention that does
|
|
33
|
-
not exist yet is worse than building against no convention at all.
|
|
34
|
-
|
|
35
|
-
## The vocabulary bridge
|
|
36
|
-
|
|
37
|
-
**Nothing in the code says bronze, silver, gold, or medallion.** The doc
|
|
38
|
-
introduces that vocabulary as a *lens*, not as names you will find. Grep
|
|
39
|
-
`oxy-internal` for "bronze" or "silver" and you get Tailwind classes for
|
|
40
|
-
leaderboard medal ranks in the observability UI (`bg-rank-bronze`); grep "gold"
|
|
41
|
-
and you get golden-snapshot test names (`crates/oxy-compile/src/compile.rs:1042`)
|
|
42
|
-
and a restaurant in seed data. **Not one hit is a data tier.** An agent that
|
|
43
|
-
greps, finds nothing, and invents a tier structure has invented it.
|
|
23
|
+
**`oxy-airway-pipelines`**; views, measures and `pre_aggregations` syntax are
|
|
24
|
+
**`oxy-semantic-modeling`**; task syntax and schedules are **`oxy-automations`**.
|
|
25
|
+
|
|
26
|
+
## The frame: bronze → silver → gold, as a lens
|
|
27
|
+
|
|
28
|
+
The medallion words are a useful way to think about how raw source data becomes
|
|
29
|
+
analytics-ready. **But nothing in Oxy — code, UI, schema names — says bronze,
|
|
30
|
+
silver, gold, or medallion.** Search a workspace for those words and you will not
|
|
31
|
+
find a tier. An agent that searches, finds nothing, and invents a tier structure
|
|
32
|
+
has invented it. Map the words onto what actually exists:
|
|
44
33
|
|
|
45
34
|
| medallion word | what is actually there |
|
|
46
35
|
|---|---|
|
|
47
36
|
| (transient) bronze buffer | `<schema>_raw.<table>` append buffer — **`replacing` disposition only**, not a tier |
|
|
48
|
-
| **silver** | `<schema>.<table>`, the normalized
|
|
49
|
-
| **gold** | `execute_sql` rollup tables (`*_daily_metrics`) **in the same schema** |
|
|
50
|
-
| the promotion | the **fold
|
|
51
|
-
| storage | Airhouse = DuckLake on DuckDB (
|
|
37
|
+
| **silver** | `<schema>.<table>`, the normalized table a pipeline lands — every disposition ends here |
|
|
38
|
+
| **gold** | `execute_sql` rollup tables (`*_daily_metrics`) built by an automation, **in the same schema** |
|
|
39
|
+
| the promotion | the **fold**: watermark-bounded, latest-wins, merge-on-read — inline at the end of a load **and again** on the scheduled vacuum, identical SQL |
|
|
40
|
+
| storage | Airhouse = DuckLake on DuckDB (Parquet on object storage + a catalog) — **for pipelines that land in Airhouse** |
|
|
52
41
|
|
|
53
42
|
That last row is a default, not a law. A rollup lands wherever its `execute_sql`
|
|
54
|
-
step's `database:` points, so gold is not necessarily DuckLake:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
warehouse.
|
|
43
|
+
step's `database:` points, so gold is not necessarily DuckLake: a gold table can
|
|
44
|
+
sit in a customer's ClickHouse with a semantic view (`datasource: clickhouse`)
|
|
45
|
+
modelled on top of it. That view is where the skills meet — a view models a
|
|
46
|
+
rollup an automation built — and it is the reminder to read the
|
|
47
|
+
`database:`/`datasource:` rather than assume the warehouse.
|
|
60
48
|
|
|
61
|
-
##
|
|
49
|
+
## Today: silver on ingest, gold in place
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
source (POS, accounting, API, …)
|
|
53
|
+
│ Airway: extract + NORMALIZE on ingest
|
|
54
|
+
│ flatten JSON → parent/child tables · snake_case · _aw_* lineage · type-coerce
|
|
55
|
+
├─ replacing? ──▶ <schema>_raw.<table> (append buffer, drained by the fold)
|
|
56
|
+
▼
|
|
57
|
+
<schema>.<table> ◀── SILVER: typed, flattened (the landed table)
|
|
58
|
+
│ automation: type: execute_sql — CREATE OR REPLACE TABLE … AS SELECT …
|
|
59
|
+
▼
|
|
60
|
+
<schema>.<x>_daily_metrics ◀── GOLD: aggregates, same schema
|
|
61
|
+
```
|
|
62
62
|
|
|
63
63
|
**Normalization happens at ingest, so the landed table is already silver.** It is
|
|
64
64
|
not a later stage. Airway flattens nested JSON into parent/child relational
|
|
65
|
-
tables, snake_cases every name
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
separate "raw → typed" step to insert your own logic into.
|
|
65
|
+
tables, snake_cases every name, coerces types, and adds lineage columns —
|
|
66
|
+
`_aw_id`, `_aw_load_id`, `_aw_parent_id`, `_aw_root_id`, `_aw_list_idx`. There is
|
|
67
|
+
no separate "raw → typed" step to insert your own logic into.
|
|
69
68
|
|
|
70
69
|
**The `_raw` buffer is a write-throughput optimization, not a tier.** It exists
|
|
71
|
-
only for `write_disposition: replacing`, to keep the write path
|
|
72
|
-
of scanning the target
|
|
70
|
+
only for `write_disposition: replacing`, to keep the write path proportional to
|
|
71
|
+
the batch instead of scanning the whole target. It is drained on fold, so steady
|
|
73
72
|
state is roughly empty — and when a fold fails the rows stay durable there,
|
|
74
73
|
which is what makes the re-run below safe rather than what makes the failure
|
|
75
74
|
harmless. **You cannot rebuild silver from it.**
|
|
76
75
|
|
|
77
76
|
> **Location trap.** The buffer lives at **`<schema>_raw.<table>`** — a sibling
|
|
78
|
-
> *schema*, where `<schema>` is the pipeline's `dataset_name`, so
|
|
79
|
-
> `toast_pos` buffers to `toast_pos_raw.orders
|
|
80
|
-
> `
|
|
81
|
-
>
|
|
82
|
-
>
|
|
83
|
-
> `migrate_legacy_raw`; the comments never caught up. **This is not version
|
|
84
|
-
> drift** — the migration predates every airway rev oxy has pinned. Follow the
|
|
85
|
-
> comment and you look in the wrong schema and conclude the buffer is missing.
|
|
77
|
+
> *schema*, where `<schema>` is the pipeline's `dataset_name`, so a pipeline
|
|
78
|
+
> landing in `toast_pos` buffers to `toast_pos_raw.orders`. Older comments and
|
|
79
|
+
> notes say `<table>_raw` inside the same schema; that was an earlier layout,
|
|
80
|
+
> migrated in place long ago. Follow one of them and you look in the wrong schema
|
|
81
|
+
> and conclude the buffer is missing.
|
|
86
82
|
|
|
87
83
|
**Gold is a `CREATE OR REPLACE TABLE` in an automation**, reading the
|
|
88
|
-
|
|
89
|
-
`toast_pos.orders_daily_metrics` beside `toast_pos.orders`. No marts
|
|
90
|
-
exists
|
|
84
|
+
pipeline-landed tables and writing aggregates into the *same* dataset schema —
|
|
85
|
+
`toast_pos.orders_daily_metrics` beside `toast_pos.orders`. No separate marts or
|
|
86
|
+
gold schema exists. The `execute_sql` task:
|
|
91
87
|
<https://www.oxygen-hq.com/docs/guide/build/automations/task-types>
|
|
92
88
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
and it is one sentence in the source:
|
|
89
|
+
**What follows from this:** there is no durable, replayable bronze for
|
|
90
|
+
pipeline-fed data. To re-derive silver you **re-extract from the source**, and
|
|
91
|
+
silver and gold sit side by side in one schema.
|
|
97
92
|
|
|
98
|
-
|
|
99
|
-
> following `execute_sql` step reads a queryable table rather than a half-folded
|
|
100
|
-
> one.
|
|
101
|
-
> — `crates/core/src/config/model.rs:2435`
|
|
93
|
+
## The sequencing guarantee — what makes this systematic
|
|
102
94
|
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
An automation's `type: airway` step completes only once the pipeline's
|
|
96
|
+
end-of-load fold has committed, so a following `execute_sql` step reads a
|
|
97
|
+
queryable table rather than a half-folded one. Ingest → rollup is therefore a
|
|
98
|
+
plain two-step automation, and the ordering is real rather than hoped-for:
|
|
105
99
|
|
|
106
100
|
```yaml
|
|
107
101
|
tasks:
|
|
@@ -110,116 +104,107 @@ tasks:
|
|
|
110
104
|
pipeline: pipelines/restaurant_analytics.airway.yml
|
|
111
105
|
- name: rollup
|
|
112
106
|
type: execute_sql
|
|
113
|
-
database:
|
|
107
|
+
database: analytics_warehouse
|
|
114
108
|
sql_query: |
|
|
115
109
|
CREATE OR REPLACE TABLE toast_pos.orders_daily_metrics AS
|
|
116
110
|
SELECT business_date, restaurant_id, sum(net_amount) AS net_sales
|
|
117
111
|
FROM toast_pos.orders GROUP BY 1, 2
|
|
118
112
|
```
|
|
119
113
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
`TaskOutcome::Failed` at `crates/agentic/airway/src/worker.rs:311`). So the
|
|
137
|
-
run goes red and the message names the cause outright: *"load wrote every row
|
|
138
|
-
but N table(s) failed the end-of-load fold … rows are durable in the staging
|
|
139
|
-
buffer but not yet visible in the public schema; re-run to fold them"*
|
|
140
|
-
(`pipeline/mod.rs:1291`). Take that literally. Cursors already advanced, so
|
|
141
|
-
*"the re-run re-extracts nothing and the fold sweeps the pending buffer"*
|
|
142
|
-
(`pipeline/mod.rs:1372`) — a full re-extract, or a Reset schema, is the
|
|
143
|
-
expensive wrong move on a run that already landed every row.
|
|
114
|
+
The task-types page documents `type: airway` (and its optional `resources:`
|
|
115
|
+
subset); what it does not say is why this guarantee is the backbone of the whole
|
|
116
|
+
pattern. Schedule the automation, not the pipeline, whenever a rollup depends on
|
|
117
|
+
the load.
|
|
118
|
+
|
|
119
|
+
**A failed fold fails the run — and the fix is to re-run, never to
|
|
120
|
+
re-extract.** Every row can be written and the load still fail, because the fold
|
|
121
|
+
that makes rows visible in the public table is a separate step from writing
|
|
122
|
+
them. The run reports correct row counts, then ends **failed**, with a message
|
|
123
|
+
that names the cause outright: *"load wrote every row but N table(s) failed the
|
|
124
|
+
end-of-load fold … rows are durable in the staging buffer but not yet visible in
|
|
125
|
+
the public schema; re-run to fold them."* Take that literally. Cursors were
|
|
126
|
+
already saved, so the re-run re-extracts nothing and the fold sweeps the pending
|
|
127
|
+
buffer. A full re-extract, or a **Reset schema**, is the expensive wrong move on
|
|
128
|
+
a run that already landed every row. Re-run the whole automation, so the
|
|
129
|
+
rollup rebuilds on the folded table.
|
|
144
130
|
|
|
145
131
|
## Rollup is not preagg
|
|
146
132
|
|
|
147
|
-
Asked to make a slow dashboard fast, an agent working in the semantic
|
|
148
|
-
reaches for `pre_aggregations`.
|
|
149
|
-
|
|
150
|
-
first thing that tells you it is the wrong tool is the SQL failing. It is a
|
|
151
|
-
**different mechanism** and it cannot do what a rollup does:
|
|
133
|
+
Asked to make a slow dashboard fast, an agent working in the semantic model
|
|
134
|
+
reaches for `pre_aggregations`. It is a **different mechanism**, and it cannot do
|
|
135
|
+
most of what a rollup does:
|
|
152
136
|
|
|
153
137
|
| | `execute_sql` rollup | semantic `pre_aggregations` |
|
|
154
138
|
|---|---|---|
|
|
155
139
|
| lives in | an automation | a `.view.yml` block |
|
|
156
|
-
| reads | any SQL — joins, CTEs, window functions | **one view**:
|
|
157
|
-
| time buckets | anything you can write | a calendar part only — `
|
|
140
|
+
| reads | any SQL — joins, CTEs, window functions | **one view**: a rollup holds only that view's own dimensions and measures, with no join |
|
|
141
|
+
| time buckets | anything you can write (`time_bucket(INTERVAL '15 minutes', ts)`) | a calendar part only — `second`/`minute`/`hour`/`day`/`week`/`month`/`quarter`/`year`, nothing between |
|
|
142
|
+
| measures | anything SQL computes | only re-aggregatable ones; `custom`, `median` and `number` measures never serve from a rollup |
|
|
158
143
|
| refresh | your automation's schedule | `refresh_key`, by a background worker |
|
|
144
|
+
| consumed by | anything that reads the table — a view, an app, a query | only semantic queries the rollup fully covers; anything else falls back to the warehouse |
|
|
159
145
|
|
|
160
|
-
So **15-minute buckets and any two-table join must be a rollup.**
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
everything else, which in practice is most gold
|
|
146
|
+
So **15-minute buckets and any two-table join must be a rollup table.** There is
|
|
147
|
+
nowhere in a `pre_aggregations` entry to name a second view or an arbitrary
|
|
148
|
+
bucket. Use preagg for the narrow case it fits: one view, calendar grain,
|
|
149
|
+
speeding up re-aggregatable measures a dashboard already queries through the
|
|
150
|
+
semantic model. Use a rollup for everything else, which in practice is most gold
|
|
151
|
+
— and model a semantic view on the rollup table so agents and apps query it by
|
|
152
|
+
name.
|
|
165
153
|
|
|
166
|
-
##
|
|
154
|
+
## Retaining raw data: a decision rule, not a preference
|
|
167
155
|
|
|
168
|
-
|
|
169
|
-
|
|
156
|
+
Whether raw data must be kept is a function of **re-extractability**, chosen per
|
|
157
|
+
source rather than applied uniformly:
|
|
170
158
|
|
|
171
159
|
| source | policy | why |
|
|
172
160
|
|---|---|---|
|
|
173
|
-
| **re-extractable** — Toast, QuickBooks, most APIs | transient `_raw` buffer is fine; **the source system is the bronze** | you can always re-pull to rebuild silver; a durable warehouse copy is redundant cost |
|
|
174
|
-
| **non-re-extractable** — video events,
|
|
161
|
+
| **re-extractable** — POS and accounting APIs (Toast, QuickBooks), most APIs | the transient `_raw` buffer is fine; **the source system is the bronze** | you can always re-pull to rebuild silver; a durable warehouse copy is redundant cost |
|
|
162
|
+
| **non-re-extractable** — video or sensor events, files people upload by hand | the raw artifact **is durable bronze and must be retained** — a table when it is an event, object storage plus a metadata row when it is a file | you cannot re-pull yesterday's frames or re-download last quarter's portal export; drop it and the history is gone |
|
|
163
|
+
|
|
164
|
+
Non-re-extractable events take a visibly different shape: written by direct
|
|
165
|
+
append into a plain, immutable table, with no `_raw` buffer and no fold — they
|
|
166
|
+
never go through Airway at all. Before proposing a retention or cleanup job on
|
|
167
|
+
any raw table, run the test above; the answer for event streams is the opposite
|
|
168
|
+
of the answer for an API source.
|
|
175
169
|
|
|
176
|
-
|
|
177
|
-
is
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
cleanup job on any raw table, run the test above; the answer for cameras is the
|
|
182
|
-
opposite of the answer for Toast.
|
|
170
|
+
**A durable bronze of files is not automatically replayable.** Re-parsing needs
|
|
171
|
+
whatever the original parse was *given*, and that is often more than the file: a
|
|
172
|
+
reporting period someone picked in a UI, text a browser extracted, a mapping
|
|
173
|
+
chosen at upload. Keep those inputs alongside the file, or the retained artifact
|
|
174
|
+
is necessary and still not sufficient to rebuild anything.
|
|
183
175
|
|
|
184
176
|
## Why the shapes are these shapes: DuckLake
|
|
185
177
|
|
|
186
|
-
`PRIMARY KEY`, `UNIQUE`, and `CREATE INDEX` are all rejected
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
`ON CONFLICT` to lean on, and scan pruning comes from
|
|
190
|
-
|
|
178
|
+
`PRIMARY KEY`, `UNIQUE`, and `CREATE INDEX` are all rejected by DuckLake, and a
|
|
179
|
+
table declaring one stops the writer. That single constraint is why `merge`
|
|
180
|
+
stages then merges and why `replacing` dedups on fold — there is no
|
|
181
|
+
`ON CONFLICT` to lean on, and scan pruning comes from opt-in partitioning and
|
|
182
|
+
sort order (`SET PARTITIONED BY`, `SET SORTED BY`), not from an index.
|
|
183
|
+
**`oxy-airway-pipelines` owns the consequences** (duplicate rows, the
|
|
191
184
|
single-flight lease, reset); do not re-derive them here. The one that reaches
|
|
192
|
-
gold: DDL in an `execute_sql` rollup must carry no constraints either.
|
|
185
|
+
gold: DDL in an `execute_sql` rollup on Airhouse must carry no constraints either.
|
|
193
186
|
|
|
194
187
|
**A corollary for rollup SQL:** join parent to child on the **propagated
|
|
195
188
|
business key** (`invoice_id` on `invoices__line`), never on `_aw_parent_id` — it
|
|
196
|
-
is regenerated every load
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
##
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
- **
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
- **
|
|
213
|
-
reasoning that has held, not something
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
## Version anchor
|
|
219
|
-
|
|
220
|
-
Checked 2026-08-19 against oxy-internal **0.5.126**, airway **0.1.32**
|
|
221
|
-
(`55e2794`), airlayer **0.3.6** (`7e884e77`) — both pinned in `Cargo.lock`.
|
|
222
|
-
Unprefixed `src/…` paths above are inside those external crates, not
|
|
223
|
-
oxy-internal; `crates/…` paths are oxy-internal. Both engines are fetched by git
|
|
224
|
-
rev, so a bump can move this ground with no oxy-internal commit — re-check
|
|
225
|
-
against the pin in `Cargo.lock`, not against memory.
|
|
189
|
+
is regenerated every load, so a join on it matches within one load and silently
|
|
190
|
+
stops matching across loads.
|
|
191
|
+
|
|
192
|
+
## What does not exist — do not build against it
|
|
193
|
+
|
|
194
|
+
These come up as "the obvious next step"; none of them is a convention today,
|
|
195
|
+
and inventing one in a customer workspace creates a second pattern nobody else
|
|
196
|
+
follows:
|
|
197
|
+
|
|
198
|
+
- **A shared conformance layer** — common `dim_store` / `dim_date` dimensions
|
|
199
|
+
and cross-source id maps, materialised once. Cross-source keys are wired per
|
|
200
|
+
domain today, mostly as hand-maintained `VALUES` tables. If you need one, build
|
|
201
|
+
it as a maintained table for that domain and say it is local.
|
|
202
|
+
- **A separate gold schema or naming rule** that tooling treats as derived. Gold
|
|
203
|
+
and silver share a schema; the `*_daily_metrics` suffix is habit, not
|
|
204
|
+
enforcement.
|
|
205
|
+
- **The retention rule as policy.** The re-extractability test above is
|
|
206
|
+
reasoning that has held, not something any tool enforces.
|
|
207
|
+
|
|
208
|
+
Checked in October 2026 against the Airway engine Oxy pins (0.1.51) and the
|
|
209
|
+
semantic engine it ships with. A newer Oxy can move this ground; trust a run's
|
|
210
|
+
own error message over this page.
|