Repository navigation
Feat/portable design standards - #77
Conversation
Run the validation and binding suites on every pull request to main, with PostgreSQL available so engine tests execute, and fail on any skipped test since a skip is a conformance gap, not a pass. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The five validation templates become *.sql.j2 reading db and semantic_db from the build context. The SQLite behavioural harness now renders them under StrictUndefined through td_context instead of reading plain files. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The registry and catalogue-interface templates read governance_db, semantic_db and stage from the build context; the shared governance container is no longer a literal. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The temporal maintenance, view, statistics and conformance specimens take the table, keys and views from the context. The access layer renders Search and Prediction grants only when those modules are in the composition. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Every Teradata template now has a render case, and a hygiene test asserts no plain .sql, no substitution convention other than Jinja, no governance container literal, StrictUndefined failure on a missing value, composition honoured, and that TEMPLATE_INPUTS.md documents every input. The memory capture protocol becomes a data-driven Jinja loop and documentation tokens in earlier templates are reworded. Reference docs follow the renamed files. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Resolve the allocator sequence name and column pii/classification defaults in the build tooling so templates read them instead of defaulting. Reject a roles setting on a platform without roles. Make validation summaries severity aware and accept an explicit run id. Add template-coverage, CLI, trust-case, mutation and lineage tests shared by every Jinja binding. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Replace the hand-written SQL, the Python model/render/checks/temporal/validate compiler and the generated Customer360 example with *.sql.j2 templates and entities.json declarations driven by tooling/bindings. Add TEMPLATE_INPUTS, rewrite the platform profile, conformance scope (draft, with declared gaps) and module and pattern bindings, add the ITSD DuckDB placement input and walkthrough, DuckDB engine tests and a binding hygiene test. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
PostgreSQL refuses an overlapping row through its exclusion constraint; DuckDB cannot, so its check must detect it. Both outcomes are non-silent. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
refactor: make Jinja the only template language across all bindings
The PostgreSQL and DuckDB bindings took their facts from per-platform inputs (TEMPLATE_INPUTS.md, context.json, placement.json) and from Python defaults. This adds those facts to the shared pipeline, sourced from the design specification, the organisation profile and the binding's own declarations, so bindings can move onto the build context. - Authoring standard §5.1: each PLATFORM_PROFILE.md declares Binding (quoting, principals, emitted roles, settings), Types (logical to physical) and Functions (expression vocabulary to SQL fragments, plus duration, map case, literals and flag test). Declarations added for Teradata, PostgreSQL and DuckDB; tooling/build/binding.py loads and validates them. - tooling/evals/expressions.py: parses the expression vocabulary to a tree and renders it through a binding's fragments; a function the binding does not declare is reported, never approximated. - Design Specification Standard: every entity has a Description section and every attribute a // description; spec_lint enforces both, and both reference specifications carry them. - Organisation profile: optional sequence role; optional roles a binding emits need templates. - Resolver: entity and attribute descriptions, physical types, enum members, vector dimensions, the canonical temporal columns each profile carries (mapped names, physical types, meanings), deletion support, relationships derived from references, standard-owned relations shaped from their module's entity definitions, emitted sequences and functions placed by the profile, expressions with rendered SQL (derivations, embeddings, metrics with inlined metric references, quality rules, filters), the orientation entrypoint container, product version (--version), and the binding's facts. - 19 new tests; schema updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion relations, optional governed views Rewriting the PostgreSQL and DuckDB bindings onto the build context showed objects the bindings had named and shaped themselves. - Standard-owned views: Semantic's ColumnCatalogue, DataProductManifest and RelationshipPaths, and Observability's TrustMap, join LineageGraph and LineageRunLatest. They are consumer views placed and named through the organisation profile. Every standard relation outside the Memory runtime facet has a consumer view. - The validation pattern defines ValidationRun and ValidationArea as entities, so a build creates them from the design; standard relations are shaped from pattern as well as module definitions. - base_view is an optional role, placed only where the binding declares it emits a governed tier (Teradata does; PostgreSQL and DuckDB do not). - A standard relation's own placement rule and mapped name apply to the relation itself (its table, or the view that is the relation); its other views follow their role rules. - Bindings declare Phases and Checks, and may emit a similarity function per Embedding block, placed by the profile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both bindings now render only from the resolved build context: shared macros, module tables from the standards, registration, discovery, capture, similarity, trust map, checks and grants, ordered by the Phases, Deploy and Checks each PLATFORM_PROFILE declares. render.py takes a specification and an organisation profile; validate.py reads physical names from the manifest and publishes wire 2.1 evidence. Both reference specifications deploy on PostgreSQL 16 and DuckDB 1.5 with every rendered check passing. Standards and tooling made consistent along the way: Decision modules, embedding shape and the Memory capture floor are linted; outside principals, Runtime scope, binding settings with defaults and procedure similarity are declared; the product registry stays in its own Semantic store; the derived trust-map entry is limited to legacy wire versions. Old per-binding inputs (TEMPLATE_INPUTS, entities.json, context.json, placement.json) are retired. The Teradata migration is not in this commit; REMAINING_WORK.md records it and the other open tasks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…_WORK.md) Partial port of the Teradata templates to the build context. Old templates are not yet retired and the old Teradata tests still target them. Predates the review fixes on feat/portable-design-standards (registration guard, discriminator-aware reference check, container-grant rule, Runtime scope). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Teradata now renders only from the resolved build context, like PostgreSQL and DuckDB, and is engine-tested on Teradata 20.00.24 (ClearScape): both reference specifications deploy and every rendered check passes. The shared engine tests run unchanged on all three engines; an engine says only how it writes a flag, an open interval end and a vector, and how it reads history. Teradata fixes the engine found: vectors are LATIN JSON-array text with a dimension check (no VECTOR type on 17.20 or 20.00.24); the admin tier gets an explicit list of rights, since ALL cannot go to a role; implied grants run before the views and are narrowed to what each view reads, tier grants run last; interval literals and NULLs in union branches are typed; maintenance casts its instants. hours_between and days_between are declared, exact to the microsecond. Conflicts between bindings, fixed in the standard and every binding, each with a test: Semantic section 5 now bounds path discovery at four hops, carried as RelationshipPaths.max_hops; a legacy run's PRODUCT trust entry is graded by Validation section 4.3 and capped at partial (PostgreSQL and DuckDB said partial even with ERROR failures). render.py now reports classification protection, object-storage placement and missing consumer point-in-time surfaces as gaps. The old flat-context templates and their tests are retired, except the graph-lineage and catalogue-interface templates, which await a decision. test_teradata_dialect.py holds every rendered statement to PLATFORM_PROFILE section 7. validate.py and the tests use one driver per engine (engines.py). Teradata docs, CONFORMANCE.md and statuses (draft) updated; the skill's roles point at the build manifest's checks; CI installs teradatasql and reads TERADATA_TEST_DSN from a secret. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Teradata now grants the governed base view of every versioned relation to the read and agent tiers, one view at a time, as PostgreSQL grants its point-in-time functions (temporal-lifecycle-metadata section 9). Base views can share a database with tables, so these are never database grants. The engine test reads the read role's rights from DBC.AllRoleRightsV: it reaches the consumer and history views and holds nothing on the table or its database. render.py counts a governed base view as a history surface, so the history gap no longer fires on Teradata. The example profiles' prose no longer says the governed views are withheld. Separating tables from views on every binding, and naming the history surface in the access-layer grant matrix, are left to their own change (REMAINING_WORK.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…duct design_lint gains platform-name: a document under design/ may name no platform, neither one with a binding under implementation/ nor one the linter lists as known. Unlike the SQL rules it has no ignore-file escape. The list of known platforms moves out of the Design Specification Standard into the linter, beside the SQL tokens, and spec_lint reads it from there, so stating the rule no longer means breaking it. design/ is made to pass: the Design Language's Appendix A, a hand-kept copy of the bindings' physical types and capabilities that had already drifted (it claimed a native VECTOR on Teradata, pgvector and vss), now points at each binding's declarations; the boundary test, the frontmatter example, the placement pattern's platform terms and two change-log entries no longer name a platform. A new rendering test shows one specification, under each platform's example profile, places the same relations and renders the same checks, with the same ids and scopes, on every binding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tooling/bindings/conformance.py runs the binding and package tests once,
attributes every outcome to the binding it evidences (an engine class's
platform, or a subtest's platform parameter), renders both reference
specifications under every example profile per platform, and writes each
implementation/*/CONFORMANCE.md: outcomes, failures, skips, every gap the
builds report, and the gaps the binding declares in a new Gaps field of its
Binding: block (Authoring Standard section 5.1). The hand-kept reports are
replaced. --check fails when a committed report is out of date and names any
gap it does not carry; CI runs it in place of the plain binding suite and
uploads the regenerated reports. A package test requires each report to be
generated and no binding document to claim standard while its report is draft.
A new engine test records the engine and driver versions the evidence comes
from.
verify_skill.py --package builds what an install receives (git archive HEAD)
and checks the entry files, every route, and that nothing .gitignore excludes
is shipped. {platform} routes must resolve on every binding, and the platforms
are discovered: they were hard-coded to Teradata.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…heck Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new engine test, run on DuckDB, PostgreSQL and Teradata, binds ITSD's Category to an existing table the organisation already has, beside an unrelated table of its own. The build must create nothing in the table's place and change nothing on it, serve its rows through the product's views, register it as the product's own, pass every check, and leave the unrelated table alone (Authoring Standard section 4.3, reference mode). It found every binding treating an adopted container as its own. PostgreSQL revoked and re-granted privileges on an adopted schema, GRANT ALL ON ALL TABLES to the admin tier included, and Teradata granted admin rights on an adopted database; both now grant only on containers the product owns. The module semantic check flagged the organisation's other tables in an adopted container as unregistered, and ColumnCatalogue published their metadata; both now use an in_product macro per binding: every object in an owned container, and only the adopted tables in an adopted one. Wrap and migrate modes, canonical names over column aliases, and Teradata's implied grant on an adopted database are declared as gaps. The conformance reports are regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Vector[n] binds to VARBYTE(n x 8), little-endian float64 checked to that length, the encoding TD_VECTORDISTANCE reads. Similarity is one stored procedure per Embedding block, running TD_VECTORDISTANCE as dynamic SQL over the store's consumer view, since the operator takes no parameters. A Deploying user setting grants CREATE PROCEDURE where it is needed; the access layer grants EXECUTE PROCEDURE to the read and agent tiers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Heads-up from #76, which is based on this PR. The two failing We would rather not delete the templates, and we would not exempt them from the gate. Our suggestion is a follow-up PR after this one merges that settles where the governance, stage and graph containers are placed (nothing in the profile standard places them today) and then ports both sets onto the build context. A tracking issue is open for it: #78. Question for you: do you want to settle port-or-retire in this PR, or are you happy for it to be handled separately? |
earthshiner
left a comment
There was a problem hiding this comment.
See my latest comments about the 2 Teradata test failures and how we might deal with them. I raised an issue #78
Portable design standards: one specification, three engines
Summary
A build now has exactly three inputs: the design specification (the design agent's output, which names no platform, container or organisation), an organisation profile (one per organisation per platform), and the platform binding.
tooling/build/build_context.pyresolves them into one build context, and every binding's Jinja templates read only that context. They derive no names and hold no product literals.PostgreSQL, DuckDB and Teradata are all on the build context. Both reference specifications (IT Service Desk and Customer Orders) deploy on all three engines, and every check the build renders passes. One specification gives every binding the same relations and the same checks, and a test holds them to that.
This PR includes PR #74 (Jinja-only bindings), which was merged into this branch rather than
main.What changes
Standards (
design/)Binding:,Types:,Functions:) that bring a platform's SQL into the context as data; a newGapsfield for gaps no check can detect; and conformance status that follows generated evidence (§8.1).partial. PostgreSQL and DuckDB had shownpartialeven with ERROR failures.ValidationRunandValidationAreaas entities. The standard-owned discovery and trust-map views are consumer views placed by the profile.base_viewis an optional role that a binding declares it emits.design/: a newplatform-namelint rule, with noignore-fileescape. The Design Language's Appendix A, a hand-kept copy of the bindings' physical types that had drifted (nativeVECTORon Teradata, pgvector, vss), now points at the bindings' declarations.Tooling
tooling/build/: the profile checker, the binding-declaration loader and the resolver.tooling/evals/expressions.pyrenders the expression vocabulary through each binding's declared SQL fragments, and reports any function a binding lacks rather than approximating it.tooling/bindings/render.pyrenders a binding's declared phases, deploy script and checks into a manifest that lists every gap.validate.pyruns the checks and publishes wire-schema 2.1 evidence through a driver per engine (engines.py), with noplatform == ...branches.tooling/bindings/conformance.pygenerates each binding'sCONFORMANCE.mdfrom the tests, the reference builds and the binding's declared gaps, replacing the hand-written files.--checkfails on an out-of-date report and names any gap it adds.tooling/skill/verify_skill.py --packagechecks what an install actually receives (git archive HEAD).{platform}routes are now checked on every binding; they were hard-coded to Teradata.Bindings
PostgreSQL and DuckDB: rewritten as templates over the build context; the old per-platform inputs (
TEMPLATE_INPUTS.md,entities.json,context.json,placement.json) are gone.Teradata: moved onto the build context and engine-tested on a ClearScape system. The real engine found and fixed:
VECTORtype on 17.20 or 20.00.24, so aVector[n]isVARBYTE(n × 8)of little-endian 8-byte floats, checked to that length.TD_VECTORDISTANCEreads VARBYTE as 8-byte floats; with 4-byte floats every distance was wrong, and no error was raised;GRANT ALLcannot go to a role;NULLin a union branch fails only when the view is queried;hours_betweenanddays_betweenare now exact.Similarity search on Teradata is now rendered: one stored procedure per
Embedding:block runsTD_VECTORDISTANCEover the store's current vectors, as dynamic SQL because the operator takes no parameters. It is engine-tested for ranking, threshold, limit and a wrong-dimension query. A newDeploying usersetting grantsCREATE PROCEDURE, which a database's creator does not get, and the read and agent tiers getEXECUTE PROCEDURE. Types may be sized as{dim*n}, and the deploy splitter keeps a procedure as one statement.test_teradata_dialect.pyholds every rendered statement to PLATFORM_PROFILE §7. Teradata now grants its governed history views to the read and agent tiers, as PostgreSQL grants its point-in-time functions.Adoption, on every binding: an engine test deploys over an existing table. It found every binding treating an adopted container as its own: changing its grants, flagging the organisation's other tables as unregistered, and publishing their metadata. All three are fixed.
Examples
examples/it-service-desk-data-product/profiles/holds the organisation profiles: PostgreSQL, DuckDB and two contrasting Teradata ones.customer360-duckdbexample and the binding-specific inputs under the ITSD example are removed: examples hold inputs, not generated output (Authoring §7).CI
.github/workflows/validate.ymlruns the validation suite, thenconformance.py --checkon DuckDB, PostgreSQL (service container) and Teradata (TERADATA_TEST_DSNsecret), and fails on any skip.docs/ci-setup.mdcovers the setup.Before merging
test_binding_packagetests fail on them alone, so CI cannot go green until this is settled. They are left untouched pending that decision.TERADATA_TEST_DSNrepository secret (docs/ci-setup.md). Until then the Teradata tests skip and CI fails, by design: a missing engine is a gap, never a pass. Then makevalidatea required check.REMAINING_WORK.md. It is the handoff note for this branch, not part of the corpus.For review
mldb.ONNXEmbeddingsreturns VARBYTE as 4-byte floats, so vectors encoded in Teradata must be converted to 8-byte floats before they are stored. Whether the function can emit 8-byte floats directly is untested.Evidence
From the last full
conformance.pyrun, on all three engines; each binding'sCONFORMANCE.mdhas the detail.Container space; the profiles'maskprotection and object-storage placement are not rendered by any binding and are reported as gaps.draft. None is gap-free, so none may bestandard(§8.1), and a package test now enforces that.How to verify
A full Teradata run takes 17 to 55 minutes, depending on the system's load.