Repository navigation
v0.9.20
Changed
- Units can carry a scale and a rate, and a guessed currency is never applied without review
(#515). Aunitnow reads<ISO code>[_<scale>][/<period>]:USD_millionsshows 12.5 as
$12.5M(scalesthousands,millions,billions,lakhs,crores),USD/dayshows 120 as
$120.00/day, andUSD_millions/yearcombines both, in answers and charts alike. There is no new
field: a release older than this one reads these strings as a plain label, so the model still loads
everywhere.suggest-unitsnow splits a name into whole words, sototal_minutes,credit_hours,
points_balanceandtotal_weightare no longer taken for money, whilegross_usd,value_usd
andfx_dollarnow are; each row gains asuggested_unitread from the name
(revenue_usd_millions→USD_millions).set-unitswithout--columnsprints its plan and
writes nothing until--yes, and gives a column whose name states a currency that unit, with
--columnstoo (so a subset of the plan is written as planned). A bare
unitscolumn (reading_units) is no longer classed additive at introspection;units_soldand
the like still are, and classes already in a model are left as they are.
Fixed
- A model outside git keeps a reviewable history of its edits (#528). agami commits a curation
only when the profile folder is its own git repository, and it never creates one, so on a fresh
artifacts folder an edit left nothing a reviewer could compare:curation_log.jsonllisted the ops
with no time and no prior value, and the datasource description and glossary writes weren't logged
at all. Every model write now logs each change with its UTC time (at), the model version it
produced (version) and, for an edit or a review-state change, the value it replaced (before)
beside the new one. The description and glossary writes are logged too, and a curate batch logs
only the ops that took, not the ones it skipped. A newsm history <root> [--limit N]prints the
log newest first, and a curate result outside git points to it. Older log lines without the new
keys read as before. The docs no longer say onboarding runsgit initor that a failed edit is
reverted through git; it is restored from backups.
Fixed
-
A column's
value_transformand a join'son:parse in the datasource's own SQL dialect, and a
text column of ISO dates gets a date format whatever it is called (#518). Both fragments were
parsed with sqlglot's generic grammar, so a valid Postgres transform such as
CASE WHEN code ~ '^[0-9]+$' THEN code::int ENDfailed validation. A fragment is now accepted when
it parses in the engine's dialect or in the generic grammar, so nothing that validated before
starts failing; a datasource whose engine can't be resolved keeps the generic parse, and the
incremental validation cache includes the dialect. A field inside a JSON column is reached by a
transform on the JSON column that extracts the key itself ((profile->>'signup_date')::dateon
Postgres,json_extract(profile, '$.signup_date')on SQLite); agami-connect now says so.
Introspection setiso8601only on columns with a date-like name; it now also sets it on a text
column with any name when at least 20 sampled values are present and every one is a real ISO date
or timestamp. Such a column no longer gets achoice_fieldskeleton. -
The model explorer counts subject areas as "subject areas", not "schemas" (#527). The totals
line at the top of the page and the line the renderer prints said "3 schemas" for three subject
areas, which reads as database schemas. Labels that really are about database schemas, such as a
cross-schema join, are unchanged.
Fixed
-
A metric built on a rejected metric is no longer served (#571). The loader left out a derived
metric whose base was dropped only when a table or column was also hidden, so with just a metric
rejected, a metric built on it stayed in the served model and validation reported "references
unknown base metric". It now runs on every load, per area and cross-area, and repeats down a
chain of derived metrics. Only a base that was rejected or hidden drops a metric: a base the model
never had is a mistake in the model, and stays for the validator to report. An ambiguity naming a
dropped metric loses that metric, as for any metric not served. The curation view is unchanged. -
A statement the database rejected says so, by its SQLSTATE (#561). An execution error none of
the classifier's patterns read came back asotherwith "an unexpected error on the server", so a
client could not tell a statement it should rewrite from a broken server, and guessed. The
database's own text still never travels, since it can carry a value from a row. Its SQLSTATE now
does: five characters, no values, and a class that means the same on every engine that reports
one, so one table covers them all without a rule per database. Class42(the statement broke a
rule),0A(unsupported feature) and22(a value did not fit an operation) come back assyntax
with the code and "rewrite it";53/54(too heavy) and40(a conflict, retry) with the code
and what to do;42501aspermission,08asnetworkand28asauthwith their usual
sentences; any other class as the code beside the usual sentence. The
code is read from the driver's exception (sqlstate,pgcode, orredshift_connector'sC
field), never from its message, and an injected executor can pass it as
ExecutorError(sqlstate=…). An engine that reports no code is unchanged, and a dialect rule (#566)
still answers first. On the forked transport asyntaxsentence is relayed only as an exact match;
the others travel as everyotherfailure does today. -
sm enrich-metadataexits 0 when no metadata preset applies (#526). Most databases carry no
in-database dictionary, and the command reported that as a failure (exit 1), which stopped a
script running it. It now says no preset applies and exits 0. Naming a preset with--presetwhose
source tables are not in the model is still an error.
Fixed
-
A SUM with a zero fallback on the many side no longer reads as a fan trap (#579). The
receipt flaggedSUM(CASE WHEN orders.status = 'shipped' THEN COALESCE(order_items.quantity, 0) END)overorders JOIN order_itemsas multiplied: the0belongs to no table, so the check that
decides which table the summed value comes from found none, and theorderscolumn in the
condition made the join look like it repeated the value. A0orNULLadds nothing to a sum
however often its row repeats, so directly under aSUM(throughCASE,COALESCE,IF,
parentheses and casts) it is now left out.COUNT,AVGand arithmetic are unchanged, and
SUM(CASE WHEN … THEN 1 ELSE 0 END), which counts the joined rows, is still reported. -
An entity is unreviewed until a person approves it (#508). The entity model defaulted to
review_state: approved, so an entity written during enrichment without the field came out
signed off, with no signer, and was trusted at query time although no one had looked at it. The
default is nowunreviewed: such an entity waits in the review queue, and approving it in
/agami-modelor withsm approve-queuerecords who signed it off and when. The shape reference
agami-connect follows showedreview_state: approvedin its entity example; it now shows
unreviewed.Existing models: an entity file that states
review_state: approvedstays approved. Only one
that leaves the field out now loads as unreviewed and appears in the review queue.
Fixed
- A join only the metadata preset assumes is not engine-approved (#509).
sm enrich-metadata
adds reference joins from two sources: the database's own dictionary, and the preset's list of
standard joins for that platform. A standard join whose values overlapped in the data was written
confirmed and approved by the engine, although this database never declared it. Such a join is
now written inferred and unreviewed, says it is the preset's standard join and not declared, and
waits in the review queue. A reference the database's dictionary declares is still
engine-approved once its values overlap, as a declared foreign key is during introspection.
Introspection already wrote every join it inferred from names and values unreviewed. New tests
now pin that for a database with no declared foreign keys.