Console template editor - #289
Open
zer0stars wants to merge 11 commits into
Open
Conversation
Types are generated from the worker's schema rather than hand-written a third time, and the vocabulary is fetched at runtime so the editor form cannot offer an option the validator rejects. A runtime guard makes the server-only rule a guarantee instead of a convention: DEFINITIONS_WRITE_TOKEN cannot reach a browser bundle without throwing. The guard is testable both ways -- definitions.test.ts runs under @jest-environment node, and definitionsServerOnly.test.ts asserts the module refuses to load under jsdom.
Trims as columns, attributes as rows, and the two contract invariants enforced in the model rather than left to the UI: an attribute lives on the template or on the trims and never both, and clearing removes the key rather than storing an empty value. Editing one trim's value for a shared attribute pushes it down first, which is what makes divergence expressible at all. Tested against the real ten-trim Camry, including the regression that powertrain_type and fuel_tank_capacity_gal are the divergent rows -- the pair the production record blended.
Every difference between what someone types and what gets stored produces a Normalisation the editor shows before save -- a reparsed number, trimmed whitespace, a cleared attribute that removes the key. The validation mirror uses the worker's own error strings so one fault is never described two ways; it is explicitly not authoritative, and the save path still surfaces the worker's 422 verbatim. Fixtures move to src/utils/__fixtures__/. Jest's default testMatch treats everything under a __tests__ directory as a suite, so a fixture module there fails as a suite with no tests.
Trims as columns, attributes as rows, agreement muted and difference at full contrast -- the inverse of normal form emphasis, so the pair of attributes the production Camry record blended is the pair the eye lands on. The divergence rail offers Move to shared at one distinct value because that is the correct action under the template-or-trim rule, and a trim with no effective selector is marked in the header rather than discovered at save.
Three states, all real. A definition with no template says so rather than erroring -- the import has not run in production and 5,152 of the emitted set carry no attributes. An id the schema cannot accept says that instead, because 137 of them are live today and subaru_tribeca-(ny/nj)_2008 is one; those are never asked about, since the answer would be a misleading 404.
The worker has one shared WRITE_TOKEN and takes author on trust. This route holds the token server-side, identifies the caller from the session Console already has, stamps author from it, and refuses a body that tries to name its own author or version rather than stripping it quietly. Entitlement follows risk: creating a template harms nobody, editing one that 4,212 vehicles resolve to re-describes all of them. hardwareTemplateId is curator-only at every tier. The If-Match forwarded is the client's, never the version this route just read -- the latter would rebase a stale editor onto whatever landed while it was open and return 200.
…anner Three places the editor leaves a trace instead of acting quietly: what it stored that differs from what was typed, why a selector-less trim will be refused before save rather than at it, and what the caller is not allowed to do and why.
Publish is disabled while the draft would fail the worker, and the fault is named in the worker's own words. The precondition is the version that was loaded, held apart from the draft so it cannot move underneath it. A conflict explains what happened and offers a reload -- it never retries over the top of somebody else's version.
Publishes immediately and attributed, because nothing points at a template nobody has created. If-None-Match makes an id that already exists a conflict with a link to it rather than a silent overwrite.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The contract has three generated mirrors (Console TS, the worker's TS, dd-api's Go). Generating them without a guard is hand-writing them one step removed: the copies still drift and nothing says so. The worker already fails `npm test` on drift; Console had the generator and no check. Two changes make that possible. The generator now reads a vendored copy of template.schema.json rather than fetching the deployed one: a generator whose input can change underneath it produces output nobody can reproduce, and drift is only reviewable when it lands in a diff. `--from <url|path>` rewrites the vendored schema alongside the types, so a contract change shows both files moving in one commit. `--check` then fails when the committed output is stale, wired into `prebuild` so a Vercel build refuses to ship stale types, and covered by a test that also asserts the guard fails on a perturbed file -- a check that cannot fail is the defect it was written to prevent.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The editor for the trim templates the extraction pipeline emits.
Trims are columns, attributes are rows. Values shared across trims render
muted; values that differ render at full contrast, so on the real Camry the eye
lands on
powertrain_typeandfuel_tank_capacity_gal— the pair theproduction record blended into one row describing two different cars.
/templatesbrowses device definitions and says which have a template, whichdo not yet, and which carry an id the schema cannot accept
(
subaru_tribeca-(ny/nj)_2008and 136 others are live today)./templates/<id>edits one. Publish is disabled while the draft would failthe worker, and the fault is named in the worker's own words.
/templates/newcreates one.The write path
DEFINITIONS_WRITE_TOKENlives only insrc/services/definitions.ts, whichthrows if it is ever evaluated in a browser — a guarantee rather than a
convention, and tested both ways.
/api/templates/[id]identifies the callerfrom the session Console already has, stamps
authorfrom it, and refuses abody that names its own
authororversionrather than stripping it quietly.Entitlement follows risk: creating a template harms nobody; editing one that
4,212 vehicles resolve to re-describes all of them.
hardwareTemplateIdiscurator-only at every tier and has no UI at all.
The
If-Matchforwarded to the worker is the client's, never the versionthe route just read — the latter would rebase a stale editor onto whatever
landed while it was open and return 200.
Needs review
unreferenced template, but backfill templates carry no
author, which under aliteral reading makes the whole catalog curator-only. This treats an absent
authoras unowned. One function changes if that is wrong(
resolveEntitlement).own needs a proposal store the worker does not have. Those callers get a
read-only editor that says exactly why.
Prerequisites
Requires definitions-worker
#4 (schema route,
compare-and-swap, generated types) deployed first. Deployment is still gated on
dd-api's
Create()/manifest()migration and a production re-run of theextraction.
Verification
203 tests passing, up from 100. This repo's
masteris red before this branch:19 suites / 20 tests fail on stale expectations unrelated to templates (a
toEqualon the whole config object, components that dropped arole). Thefailing set is byte-identical before and after this branch.
npx tsc --noEmitreports zero errors under
src/.npm run buildsucceeds.Note:
CLAUDE.mdsaysnpm run compileis a typecheck; it isgraphql-codegen.