Skip to content

Python data-dict.yaml reader and the three prompt channels - #253

Merged
jat255 merged 3 commits into
mainfrom
jat255/m2-data-dictionary
Sep 2, 2026
Merged

Python data-dict.yaml reader and the three prompt channels#253
jat255 merged 3 commits into
mainfrom
jat255/m2-data-dictionary

Conversation

@jat255

@jat255 jat255 commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Third PR of M2. Reads the authored data-dict.yaml into pydantic models and renders the three channels a dictionary feeds. Closes the dictionary issue for M2.

What it reads

The input is the authored YAML, never data-dict's JSON export, so what is validated is the published data-dict spec. Parsing is permissive about fields commons never inspects and strict about the shape it does: a number(quantity) column with no range reads without complaint, because nothing reads that range. Tables and columns may be authored as sequences with a name field or as a pre-keyed mapping, and both key the same way.

Prose stays as authored markdown, because it reaches the model verbatim.

The three channels

They are methods on the dictionary rather than separate structures: ambient_prompt_text() and ambient_glossary_terms(), entry_text(table) for first touch, and context_chunks() for the search index.

pkg-r spreads the same rendering across R/data-dictionary.R, R/prompt.R and R/context-layer.R. Putting it on the dictionary means the prompt and context layers call in rather than reaching into its internals. That is a difference in shape, not in what either produces.

Glossary entries are ambient up to a size cap, and entries past it are co-resolved at first touch when an entry mentions them, so capping loses nothing. Column-level content deliberately stays out of the search index: first touch owns it, and indexing it would pay for a second copy the agent already has.

Known gap: Governed definitions are parsed but not yet rendered

definitions in a data-dict are parsed into the model, and neither channel renders them yet.

This is a known gap at this point and is staged to be implemented in a later task on M2. First touch shows a definition's compiled SQL, never the expression authored in the data-dict file, which pkg-r's own test asserts by checking the expression is absent from the entry. Nothing correct can be shown until the compiler produces export records, so both channels will be wired in at that point. Comments mark both seams.

For the same reason expr is optional on the model. pkg-r validates it in the export spec, not in the reader, and aborts the whole read when it fails. Checking it in both places would be two spellings of one rule to keep in step.

Two reserved keywords

tables and dictionary are explicit keyword-only parameters, so both names are reserved in every form of data_source(). A frame passed under either name is rejected with a TypeError naming it, never silently consumed. Frames genuinely named tables remain available via DataSource.from_frames().

Verification

274 tests, ruff and pyrefly clean.

The input is the authored YAML, never data-dict's JSON export, so what is
validated is the published data-dict spec. Parsing is permissive about
fields commons never inspects and strict about the shape it does: a
number(quantity) column with no range reads without complaint, because
nothing reads that range. Prose stays as authored markdown, since it
reaches the model verbatim.

The three channels are methods on the dictionary. pkg-r spreads the same
rendering across data-dictionary.R, prompt.R and context-layer.R; here the
dictionary owns it and the prompt and context layers will call in. That is
a difference in shape, not in what either produces.

Glossary entries are ambient up to a size cap, and those past it are
co-resolved at first touch when an entry mentions them, so capping loses
nothing. Column-level content stays out of the search index: first touch
owns it, and indexing it would pay for a second copy the agent has.

`dictionary` applies to every source form, so unlike `tables` it is always
an option rather than a possible frame name. Passing a frame there fails
by name rather than being silently dropped.
They render as compiled SQL rather than the authored expression, so there
is nothing correct to show until the compiler can supply records.
@jat255
jat255 marked this pull request as draft September 2, 2026 00:45
@jat255
jat255 force-pushed the jat255/m2-data-dictionary branch from e4fa2c0 to bf66272 Compare September 2, 2026 00:45
@jat255
jat255 changed the base branch from jat255/m2-engine-board-sources to main September 2, 2026 00:45
Both names are now reserved by the signature itself rather than by hand
in **kwargs handling, matching the signature the port plan sketched. A
frame passed under either name fails with a TypeError naming it; frames
genuinely named 'tables' remain available via DataSource.from_frames().
@jat255
jat255 marked this pull request as ready for review September 2, 2026 03:06
@jat255 jat255 added the py Affects the Python implementation label Sep 2, 2026
@jat255
jat255 merged commit 2ed023f into main Sep 2, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

py Affects the Python implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant