You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TACC uses ui-react at mint.tacc.utexas.edu in place of the Lit UI, and the client
manager signs off that the SOW's acceptance criterion (§8 — a model input's variable,
standard variable and unit configured in one form submission) is met.
That sign-off closes out the SOW. The map ends there.
Notes
Domain. MINT / SUBSIDE model catalog. See ADR-0002
for the React migration and ADR-0001
for the Hasura backend it sits on. Both have a Traps section — read it before debugging
anything that looks like a permissions or cache bug.
This map carries execution, not just decisions. The remaining work is deploy-and-verify,
so tickets do the thing rather than only decide it.
Contract. The SUBSIDE model-catalog UI SOW with UT Austin / TACC. Its period of
performance has expired (confirmed not to be a blocker) and the engagement is settled
financially, so the only remaining obligation is delivery and sign-off.
Contract number, terms, amounts, billing status and client contacts are deliberately not
recorded here — this repo is public. They live in the private metalearn wiki. Keep them
out of this map and its tickets.
Standing preferences for this effort
Deploy target is TACC's own small k8s cluster, via the MINT Helm chart. Max has kubectl access.
ui-reactreplaces Lit at mint.tacc.utexas.edu (decided; T1 de-risks it).
No UAT walkthrough document — TACC gets the URL (decided).
Skills: /grilling and /domain-modeling on decision tickets.
State at charting (2026-08-08). Last code commit 2026-07-20; ~3 weeks idle. ui-react is
containerized and published, 21 routes ported, and has run only on Vercel previews and
localhost. OAuth2 has been exercised on previews and works — but previews use a dedicated
OAuth path for preview origins, so the round-trip at mint.tacc.utexas.edu is still unproven.
The app has never been driven against TACC's own Hasura and CKAN.
Decisions so far
Confirm SUBSIDE can live without the un-ported Lit screens —
none are needed; the flip is free. Most are already dead or stubbed in Lit, Analysis and
Emulators have no nav link, thread Summary turned out to be ported, and thread Visualize was
never functional in Lit either. Also settled the rollout: no parallel deploy (no users on
Lit at TACC), Vercel is the test surface, and Lit stays deployed-but-unrouted at the upgrade
as the only undo, removed after sign-off.
Tag helm-charts 9.0.0-beta.3 and bump the submodule pointer off beta.2 —
the chart is reachable again. 9.0.0-beta.3 tagged upstream on 3d3aca8, submodule bumped
in #82, merged to develop as 200a0d7.
Both confirmations held: ui_react.enabled: false by default, and the legacy ui
component is untouched (values.yaml is +52/-0, no ui* template in the diff, _helpers.tpl
append-only). Three facts fell out — ui_react needs an OAuth2 client distinct from
Lit's (one callback URL per client, so reusing Lit's breaks the undo — sharpened #73); its image tag is a monorepo SHA
the bump automation will silently skip, so it is hand-set; and it takes its own host
rather than a path prefix, making the flip a host reassignment.
Verify CKAN from a browser origin against ckan.tacc.utexas.edu — is CORS actually broken? —
fine in production, broken on Vercel. CKAN echoes Access-Control-Allow-Originonly for *.tacc.utexas.edu (not the apex), localhost and 127.0.0.1; every other CORS header is
sent to everyone, so it looks enabled when it is not. mint.tacc.utexas.edu is allowlisted,
so production needs nothing from TACC and nothing from us — which killed #74 (the nginx proxy only serves the
origin that already works, and never touches a preview), closed unopened. *.vercel.app got
no header — since fixed: Max added the two real Vercel origins to CKAN's nginx map as
anchored regexes and dropped the pre-existing *.github.io wildcard, verified live on every
action the app calls plus the preflight. So #76 runs wholly on the Vercel preview as
originally written, /datasets/* included, on the branch alias monorepo-git-develop-mosoriobs-projects.vercel.app — per-deployment preview URLs are refused
and cannot be allowlisted, since the hash changes every push. No public-suffix wildcard is left
in the allowlist, which closes the credentialed-access concern the first fix opened.
Commit .env.example and local-setup notes so ui-react runs for someone who is not Max —
a .env file cannot configure the dev server. That was the finding, and it made the
ticket's premise wrong. index.html always loads public/env-config.js, so the VITE_*
fallbacks never run. The real fault was one line: HASURA_ENDPOINT pointed inside the
cluster, at graphql.mint.local. It now points to TACC's public Hasura. A clone plus npm install plus npm run dev now shows real model catalog data, read-only, with no
cluster access. I verified this live from a localhost:3000 origin. The ticket also added .env.example, npm run config:local and a README section, and deleted .env.development. It deleted MODEL_CATALOG_API, which no code used. That left #75 with a fixed list of 12 keys. ENSEMBLE_MANAGER_API must be set, or thread execution stops without an error. The code is
in PR #83, merged to develop as 45a6d7e.
Check whether the OAuth2 redirect URI for mint.tacc.utexas.edu is already registered — already registered; nothing to do at the IdP. The premise was wrong: Lit uses /oauth2/callback too (${window.location.origin}/oauth2/callback, hardcoded), so both apps
want the identical redirect URI. The IdP is Tapis, tenant portals — not Keycloak — and
client mint has callback_url exactly https://mint.tacc.utexas.edu/oauth2/callback.
Verified live, with negative controls, against GET /v3/oauth2/authorize, which validates redirect_uribefore login: the match is exact on scheme, host and path (http:// is
rejected), and no mint-*react* client exists. Tapis really is one-callback-per-client, but
the Tag helm-charts 9.0.0-beta.3 and bump the submodule pointer off beta.2 #70 inference that ui_react needs its own client does not follow — the two apps want
the same URI and never serve the origin at once (Lit is unrouted). Decision: reuse mint,
so there is no IdP change at flip or rollback. The chart's own text argues the opposite and
is wrong for this rollout; it only hard-fails on an emptyclient_id, so "mint" renders —
flagged on #75. Two constraints landed on #77: the host must be exactly https://mint.tacc.utexas.edu, and Lit must be unrouted, never re-hosted — parked at a
second hostname it cannot log in, making it a rollback to a broken app. Also rules out a
staging host for ui-react before the flip.
Settle the window.MINT_CONFIG values for the TACC deployment — two values, not twelve. The deployed Lit app publishes TACC's real config at mint.tacc.utexas.edu/config.js, so no value had to be guessed. Ten of the twelve keys need
no statement: they derive from the component ingresses or inherit shared chart values TACC
already overrides (google.maps.key, welcome_message). Only client_id: "mint" and data_catalog_api: "https://ckan.tacc.utexas.edu" must be set. Derivation is proven safe
for Hasura and the Ensemble Manager rather than assumed — the Lit template and the ui_react
helper share the same ingress.tls idiom, and Lit's live output shows https:// for both, so
TACC declares TLS and there is no mixed-content trap. CKAN is the exception: ui_react derives
it from components.data_catalog, which TACC does not use (it uses external_services.ckan.url,
read by the Lit template only), so the key is omitted and the app falls back to a compiled
default that is right by coincidence — hence the override. Verified live from the production
origin: Hasura and the Ensemble Manager both send Access-Control-Allow-Origin: *, the
Ensemble Manager's /v1 routes exist, and CKAN sets no X-Frame-Options, so the datasets
iframe will render. Two facts landed on #76 — ui-react needs no CKAN API key
(Lit's DATA_CATALOG_KEY has no counterpart; all reads are anonymous GETs), and ENSEMBLE_MANAGER_API is now set, so thread execution is in scope for the smoke test. Three
chart cleanups landed on #77: the dead MODEL_CATALOG_API key, the wrong "must be a distinct client" text, and teaching ui-react-config.yaml to read external_services.ckan.url. None block the flip.
Ensemble Manager calls send no Authorization header — localStorage key mismatch —
fixed in PR #87, merged to develop as 4f7debf.
Both REST calls read the token by string literal from a key nothing writes, and spread the
header conditionally, so every call went out anonymous with no error. The symptom is now known
rather than guessed, from the Ensemble Manager source: against a tapis engine, which is
what TACC runs, submit returns 500 ("Unauthorized" escapes as a plain Error) and logs
return 401 — both read as server faults, not a missing credential. The probe could not
settle this because fetchLog looks the execution up before it reads the header, so an
unknown id 404s either way. Both calls moved into src/lib/ensemble-manager.ts so the header
is built once; the tests assert the outgoing header, not the storage key, and were checked
to fail against the old code. The fix exposed a second defect and did not resolve it: #88 — ui-react posts to /executionEngines/localex, and the only such route is /executionEngines/tapis, so run
submission 404s before auth matters. Submission is still broken at TACC; the log call is
fully fixed.
ui-react submits runs to /executionEngines/localex, a route that does not exist — two faults, not one. The engine name was wrong for TACC, and the route was wrong for every
engine except Tapis: server.ts mounts /executionsLocal, /executions and /executionEngines as three separate routers, and the last registers /tapis only. So a
config key alone would not have fixed it. The engine is now the config key EXECUTION_ENGINE, and executionEnginePath maps each backend to its real route — the same
map Lit makes in mint-runs.ts:731 — so the key is honest for all three engines. The
default is localex, matching both the chart default for the Ensemble Manager and the Lit
fallback, so one unset value describes the same deployment in three places. The results step
needed nothing: MintResults reads the engine off the execution row, not from config, and its localex/wings rewrite is dead because MintThread never passes mintConfig. Code is in PR #89, merged to develop as 1d1ada9.
One constraint landed on #77: the chart must
emit EXECUTION_ENGINE: tapis for TACC, derived the way ui-config.yaml derives Lit's — a
prerequisite for run submission, not a cleanup. Both halves confirmed live on #76: TACC's Ensemble Manager publishes
exactly the three separate mounts predicted here, and its Lit config sets REACT_APP_EXECUTION_ENGINE = "tapis".
Smoke-test ui-react against TACC's real Hasura and CKAN — the §8 acceptance criterion passes, for a new input. Signed in as mosorio@portals, a new
model input took label + standard variable (soil__porosity) + unit (m3 m-3) in one
submission against TACC's live catalog. The form is genuinely flattened — one control for
standard variable and unit, which offers the units already used with the variable chosen. The
cmdk trap ADR-0002 warns about could not apply: this picker is plain buttons, not cmdk. Verified
anyway by hit test and a real mouse click. Run on localhost:3000, not the Vercel preview —
the preview is behind Vercel SSO, and localhost is CKAN-allowlisted with a working OAuth
client, so it tests strictly more. Editing an existing input fails, and that is #91, now blocking #77: the junction upsert names a column this
repo's own metadata never exposes, so it is not TACC-specific, and it half-applies. A second,
minor fault rides along — a successful save renders stale until a manual reload. Thread
execution was carved out as #92 rather than
fired at the client's cluster unasked. /modeling/* renders against real data; note the region
filter defaults to California, where TACC has nothing — it reads as an empty deployment until
you switch to Texas. All test rows were removed; TACC's catalog is back to its prior state.
Editing an existing model input fails: the junction upsert names a column Hasura does not expose — fixed and verified live; the write surface is a graphql_engine question, not a TACC one.
Client-only, so nothing to apply to TACC's Hasura. SetConfigurationInputOptional replaces the
upsert with delete_by_pk + insert_one in one document — Hasura runs a mutation's root
fields in a single transaction, so the row is never left missing — and it now fires only when is_optional changed, instead of for every row in toUpdate. Proven under a real Tapis token
with a zero-write probe: validation precedes execution, so the old document returns validation-failed (the exact _PLACEHOLDER error) and the new one reaches execution. Why
nothing caught it:generated/schema.graphql is the admin schema, where the column is
updatable — codegen and all 760 offline tests validate against a schema the app never runs as.
Treat any role-sensitive write as unverified until it runs under a real token. Fault 2 fixed too:
the form refetches GetConfiguration before onSaved, so a save no longer renders stale.
End-to-end on simpleModelAnnotated — add, edit label, toggle Optional — all clean, and the
catalog was returned to its exact prior state. Code in PR #93. One thing deliberately not fixed:
the save is still many mutations, not one transaction, so a mid-sequence failure still
half-applies. No step on this path can fail that way now; ticket it if TACC hits it.
Thread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field — fixed and verified live: 3 of 40 annotated variables → 40 of 40. Client-only; nothing to
change at TACC. findDatasets no longer sends a q at all — packagesMatchingVariables keeps
the packages whose resources carry the variable, as Lit does. The ticket's premise was
re-measured first and held. Three defects the ticket did not name, each real: resource
narrowing was mandatory, not cosmetic — a dataset matches because some of its resources carry
the variable, and one TACC package holds 35 resources, 1 of which does, so binding all of
them handed the model input files it cannot read; datatype was read off the first resource,
which MintDatasets then filters the list on; and rows=1000 was a silent cap, now paged. Two existing fixtures encoded the bug — they carried no annotation and passed only because
the old code matched prose. Code in PR #95,
CI green. Submit one thread run end to end against TACC's Ensemble Manager #92 is unblocked, with one constraint landed on it: ext_bbox drops any package
with no spatial extent, and 11 of TACC's 33 annotated packages have none — Texas keeps 16 of
33, the California default keeps 0. Lit behaves the same, so it is not a regression, but a
region-scoped thread cannot see a third of the annotated catalog — now #97. The same root cause survives
on the browse surface as #96, deliberately
not fixed here.
Dataset search by variable name searches CKAN free text, not mint_standard_variables — fixed and verified live: groundwater 48 → 12, corpus_nlp 0 → 17. Client-only; nothing to
change at TACC. The measured premise held exactly. Substring-match the annotation, prose
dropped — chosen over keep-prose-and-badge, which leaves the count wrong and cannot fix corpus_nlp at all (Solr splits the name on _ before it matches), and over a typeahead on
Hasura's 668 standard variables, where only a small fraction are carried by any TACC dataset, so
a user picks a legitimate variable and gets a confident 0. Substring matching only ever offers
what exists, and it agrees with the Datasets step: show the datasets the region filter hides, instead of dropping them #97 rule: filtering by standard variable correctly drops a
dataset that carries no annotation. A second, independent fault rode along and hit both modes: searchDatasets took CKAN's rows=100 default while TACC holds 215 packages, so the empty term
listed 100 of 215 and said nothing — now searchAllPackages, which Thread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field #94 had already taught to
page. buildSearchQuerylost its variables branch entirely; it only ever built a query Solr
cannot answer, and this was the second surface it broke. One ticket framing was wrong: this was
never a regression. Lit's own /datasets/search is dead at TACC — it POSTs to data_catalog_api + "/find_datasets" (ui/src/screens/datasets/actions.ts:377), the legacy MINT
Data Catalog route, never routed through DataCatalogAdapter, so data_catalog_type = "CKAN"
does not redirect it; TACC answers 405 for both modes. So "match Lit" was not available as a
constraint. Code in PR #100, full suite green
at 813 tests; the 22 new assertions were checked to fail against the old code, on a path that had
no test file at all.
Thread wizard shows 18 of 175 model configurations: the model tree query accepts only sdm#Model — no type belongs out of the model tree; all three queries drop the predicate. Verified live in
the signed-in app: the Models step now reports 175 where it reported 18, and HAND — TACC's
largest family, 44 configurations — goes 0 → 44. The ticket's premise was wrong. type classifies a model; it does not say whether a row is one. The ETL only migrated ?id a sdm:Model and stored the most specific subtype, so every row is a model by construction,
and the 8 sd#Software rows carry the ontology superclass — MODFLOW ×5, ParFlow, an InSAR
workflow, a particle filter. So _neq sd#Software, the ticket's own candidate, would have hidden
real models. An allowlist is worse and this was measured, not argued:model-catalog-api
keeps one and it is already stale, serving 44 of 55 — #102,
ruled out of scope below. All three sub-questions answered the same way: GetModelFamilies too
(registration could not offer any subtype family, so no version could be registered under HAND),
and RegionModels too (it shares the query, so a different rule would only make them disagree).
Registration still writessdm#Model for a new family, which is correct — read and write are
different questions. Code in PR #101, 802
tests green, the 9 new assertions checked to fail against the old code; graphql.ts regenerated
by codegen from the committed admin-schema snapshot rather than hand-edited, which is worth
remembering — codegen runs offline against generated/schema.graphql, no admin secret needed. Submit one thread run end to end against TACC's Ensemble Manager #92 is unblocked. The fix also exposed the next wall in the same step, now #103: the Variables step offers 668
indicators where 147 can find a model.
Thread wizard dead-ends at Parameters: threadExecutionData is never loaded — the pipeline now loads from Hasura, and the ticket's premise was half the fault. threadExecutionData was useState(null) that nothing loaded, as written — but DatasetsStep persisted nothing either, calling UpdateThreadData with empty data
and modelIO against a mutation that deletes every binding first. So Continue destroyed
bindings, and Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104's own measurement that the assignment survived a reload is retracted.
That mattered: loading the pipeline alone would have changed nothing, because there was
never a binding to read back. GetThreadExecution + threadExecutionFromGQL are ported
from Lit's get.graphql and graphql_adapter.ts; Datasets and Parameters now write;
executions load and the summary polls while runs are in flight. Three defects found in
the wiring, all where the React port had drifted from Lit: resource ids were a djb2 hash
where Lit writes MD5 of the URL — and since this ticket is what starts writing those rows,
shipping it would have duplicated every file TACC already had; run_progress is a
fraction, drawn as a percentage, so a finished run rendered as a 1% sliver; and total_runs
dropped the input-resource factor, where TACC's own summaries keep it. Verified live three
ways — anonymously for the whole thread_models subtree and the executions query, under a
real Tapis token for thread.thread_data and dataslice.dataset (both user-only), and a
zero-write probe of the parameters mutation that reached constraint-violation rather than validation-failed. The running app renders Runs ✓ Complete from TACC where every step
read Pending before. Code in PR #105,
852 tests green. generated/schema.graphql is the admin schema — the role trap Editing an existing model input fails: the junction upsert names a column Hasura does not expose #91
found for writes applies to reads too: anonymous sees neither thread_data, nor thread.events/permissions, nor the dataset table. Submit one thread run end to end against TACC's Ensemble Manager #92 is unblocked, and carries
the one thing Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 could not prove: the two write round-trips through the UI.
Datasets step: show the datasets the region filter hides, instead of dropping them — two faults, opposite in direction, and the ticket named only one.ext_bbox hid the 11 of
33 annotated packages with no extent, as measured — but MintThread also never passed the
region to DatasetsStep at all, so what did arrive was not narrowed either. One rule had to
settle both, and does: unknown extent is shown and badged ! no location; known-but-outside is
hidden behind a counted link. findDatasets now drops nothing for the region — it labels each
dataset region_match: inside | outside | unknown and the UI decides. Verified live at TACC: Texas 29 offered (18 in-region + 11 badged) and 4 behind the link, where ext_bbox served
16; California — the default framing — 11 badged rows where it showed 0, which retires the
"reads as an empty deployment" note. ext_bbox is not a server-side version of the same
test: ckanext-spatial indexes bare geometries only, so it also drops a Feature or FeatureCollection, and TACC has two of those squarely inside Texas — hence 16 → 18, not
16 → 16. The extractor replaced two partial readers, not one: the region map's calculateBoundingBox missed Feature.geometry and FeatureCollection.features too, so lib/geo/bbox.ts now serves both surfaces and the region picker gained the coverage for free. GetThread selects region { geometries }, confirmed readable under anonymous. Code in PR #113, 883 tests green; the
Feature/FeatureCollection/Point assertions were checked to fail against both old readers.
Two traps left standing, neither blocking: a bounding box is not the region — Alaska crosses
the antimeridian, so its box spans nearly the whole globe and almost nothing reads as outside it
(Lit is the same; it does not bite Texas) — and the browse surfaces still carry the old fault,
now #114.
Deleting a problem statement, task or thread leaves the row behind and reports success — the permission was unsatisfiable by construction, so this is the first fix on the map that
needs a schema migration. The user role may delete one of these rows only while it still
carries a CREATE provenance event; the provenance FKs were ON DELETE RESTRICT. Delete the
provenance first and the row loses its own delete permission (0 rows, reported as success);
delete the row first and Postgres refuses it. Both orders were run against a live database
and neither works, which kills the ticket's own first option and rules out any client-only
fix. The ticket named one failed root field; all eleven failed — every child on the tree is
gated through the same provenance, so only the provenance and permission rows were ever really
deleted, which is exactly the state Submit one thread run end to end against TACC's Ensemble Manager #92's orphan was found in. Six FKs become ON DELETE CASCADE in graphql_engine PR #14 — metadata unchanged, structural FKs untouched — and the client deletes bottom-up, never
touching provenance, in PR #115 with assertDeleted to stop a zero-row delete rendering as success. A second defect had to be fixed
with it: the port dropped Lit's dataslice/thread_data deletes, harmless while the
mutation was a no-op but a hard FK error once Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 started writing those rows. Verified on the
migrated dev cluster under Hasura's own compiled predicates, in a rolled-back transaction; the Submit one thread run end to end against TACC's Ensemble Manager #92 orphan is cleared. Two things outlive this ticket. Lit's delete mutations are
byte-identical, so delete is broken at TACC today — which is why Deleting a problem statement, task or thread leaves the row behind and reports success #99 no longer blocks #77 (decided by Max, 2026-08-09, on the
same rule Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 set: a defect blocks the flip only if moving off Lit is a regression). And the
chart's Hasura migration job is a post-install hook only, so helm upgrade never applies
migrations — TACC needs the six-FK change run by hand, after the flip, preferably as a surgical run_sql rather than hasura migrate apply, whose state at TACC is unverified.
Apply the modeling provenance cascade migration to TACC's Hasura — delete works at TACC, and the migration alone was never enough. The schema change was applied
on 2026-08-10 (six FKs read confdeltype = c, no drift, one 100-day idle in transaction session
had to be terminated first — Set idle_in_transaction_session_timeout on TACC's Postgres #119, runbook docs: runbook for the TACC cascade migration #118). Step 4, the round trip under a real Tapis token,
is now done: a throwaway problem statement created, deleted and gone after a reload, with delete_thread and delete_task at affected_rows 1 and delete_problem_statement_by_pk
returning a row — the value Deleting a problem statement, task or thread leaves the row behind and reports success #99 could never get. The successful delete is itself the proof the
FKs are CASCADE, since under the old RESTRICT it raises a foreign-key violation; that is the
migration verified from the app, not from confdeltype. Confirmed as anonymous afterwards,
not just in the app — an orphan is invisible to its owner and visible to anonymous, which is how Deleting a problem statement, task or thread leaves the row behind and reports success #99 hid. Run at localhost:3000 against TACC's production Hasura, not at the host, and the
literal test was not available:mint.tacc.utexas.edu still serves Lit, whose delete removes the
CREATE provenance as root field 6 before the row delete in the same transaction, so it fails for
a reason unrelated to the migration — a false negative. Origin is not part of this round trip (same
endpoint, same database, same user role); what localhost cannot prove is the deployment, which is Flip mint.tacc.utexas.edu to ui-react #77's job, and a re-confirm-after-flip constraint is landed there. Two things outlive this
ticket. The migration alone does not fix delete — the client fix fix(ui-react): delete a problem statement, task or thread bottom-up (#99) #115 is required, and Lit
stays broken, so a rollback to Lit is a rollback to silent orphaning. And a stale dev server from
another worktree held port 3000, so two runs exercised day-old code that looked right and produced
a confident wrong diagnosis; the request body gave it away, not the UI. Start dev servers with --strictPort, and read the request body before believing a live verification. That accident was
a clean negative control — the old client against the migrated database reproduced Deleting a problem statement, task or thread leaves the row behind and reports success #99 exactly,
HTTP 200 with no errors key — and it left two orphan rows at TACC that no client can delete,
now #120.
Models step Continue does nothing once a thread has execution data — fixed and verified live; the step now saves by diff, and Lit's answer was the wrong one to copy.
Both faults held as written: the mutation deleted everythread_model row for the thread against
four ON DELETE RESTRICT children, and ModelsStep had no catch, so the refusal was silent. diffThreadModels keeps a still-selected row and its id, deletes only deselected rows with
their four children, and inserts only what is new — so an unchanged Continue writes nothing at
all. Keeping the id is the whole point: the children hang off thread_model.id, so no
delete-and-reinsert can preserve them. Lit clears the wall by deleting every child row for the
thread on every save, which destroys the Datasets and Parameters work each time — not copied.
Verified on thread x4gbmq00msmbp9ib under a real Tapis token, reading request bodies: unchanged
Continue sent zero GraphQL requests and advanced the step; adding a model sent removedIds: []
with all five deletes at affected_rows 0; removing it sent models: [] and delete_thread_model
at affected_rows 1; and the kept row's id, data binding and total_runs 2 were untouched. TACC is
back to its exact prior state. Three things outlive it.insert_<table>(objects: []) is
accepted under the user role — Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 implied it, this measured it. 21 of TACC's 109 thread_model rows carry no modelcatalog_configuration_id, and the step deliberately leaves
them alone: deleting one would hit the same RESTRICT wall on exactly the legacy threads that predate
ui-react. And removing a model still discards its runs without warning — Lit does the same for every
model on every save, so it is not a regression; ticket it if TACC hits it. Code in PR #121, 921 tests green, the 4 new assertions
checked to fail against the old code. Flip mint.tacc.utexas.edu to ui-react #77 is down to one blocker, #110.
The model-tree read surface has the same fault in two more places.Settled on #98: all three queries fixed together, and
a grep over ui-react confirms they were the only ontology-type filters in the app — so the
open half of this patch ("does any other query filter this way") is answered: none do.
Whether TACC's Hasura carries the same permission gaps as ISI's. Partly measured while
resolving #71 — the endpoint is https://graphql.mint.tacc.utexas.edu/v1/graphql, it sends Access-Control-Allow-Origin: *
(so Hasura is reachable from any origin, Vercel included — the CKAN allowlist is not
mirrored here), and anonymouscan read modelcatalog_standard_variable and modelcatalog_unit, unlike ISI. But *_aggregate is absent for anonymous, same as ISI,
so there are still no server-side facet counts. The read half is now settled — the
anonymous smoke test on #76 drove /models, /variables and the Output variable facet against TACC with no login and found no
gap. ADR-0001's warning about anonymous and modelcatalog_standard_variable does not apply
to TACC. The write surface is now partly measured too, and the answer was not a TACC gap: #76 wrote a dataset specification, a
variable presentation and a junction row under a Tapis token, and deleted all three. The one
write that failed, #91, failed from this
repo's metadata, not TACC's — so stop framing the write surface as a TACC unknown and read it
as a graphql_engine question. Now fixed, client-side, with no metadata change, and the
edit path verified live end to end. What stays unaudited is the rest of the user role's write
permissions against what the client sends.
~~Whether TACC's catalog holds the ~303 standard variables and ~107 units the typeahead
assumes.~~ Settled on #76: TACC holds 668 standard variables and 129 units, counted by paging since anonymous has no *_aggregate. Both are well above the assumption.
Whether anyone but Max can run the authenticated app outside production.Fixed. Tapis
client mint-localhost-3000 now serves http://localhost:3000/oauth2/callback and is the
repo's default AUTH_CLIENT_ID; mint-local keeps http://mint.local/oauth2/callback for the
dev cluster. Clients are named after the origin they serve, since Tapis allows one callback per
client. A real login on localhost succeeded — and immediately exposed #85, a silent auth defect no amount of
anonymous testing could have found — which in turn exposed #88. One real login has now produced two
defects; treat that as the rate to expect from the authenticated walkthrough.
Out of scope
Porting the un-ported Lit screens — Analysis, Emulators, Messages, models-compare/models-calibrate/models-cromo, thread Visualize, and the 3,256-LOC model-view. (Thread Summary was on this list in error — it is ported.) Never in the SOW,
whose scope is the config workflow and the flattened form. T1 confirmed SUBSIDE can live without
them; it does not port them.
Retiring ui/ entirely. Downstream of the above, and a separate effort.
ISI's mint.isi.edu cutover. Different host, different stakeholders, not this contract.
ADR-0001 leftover debt — old model/model_io/model_parameter tables, Fuseki
references in docker-compose and CI, absent row-level security on modelcatalog_*.
A written UAT walkthrough. Considered and declined; TACC gets the URL.
The same model-type fault in model-catalog-api — #102: /models returns 44 of TACC's 55
software from a stale subtype allowlist, and four custom-handlers.ts handlers are cut harder
still, to 27. Real and measured, surfaced while resolving #98. Out of scope because nothing on the path
to sign-off touches it: ui-react talks to Hasura directly and never calls that API (Commit .env.example and local-setup notes so ui-react runs for someone who is not Max #72 deleted
the dead MODEL_CATALOG_API key), and the only consumer is the Lit UI, which stays unrouted and
is being retired. Filed so it is not lost.
A ZIP component cannot run on Tapis — #108: the Ensemble Manager's Tapis path
JSON-parses has_component_location, so a model whose component is a localex ZIP (HAND) fails
with an opaque 500. Surfaced on the first attempt at #92 and proved by negative control — the
same 500, byte for byte, with the input dataset swapped. Out of scope because it is
pre-existing server behaviour, Lit hits it identically, and nothing on the path to sign-off
needs a ZIP component: Submit one thread run end to end against TACC's Ensemble Manager #92 finished by choosing a Tapis-backed model instead. It does not
block Flip mint.tacc.utexas.edu to ui-react #77.
Three smaller defects found while resolving Submit one thread run end to end against TACC's Ensemble Manager #92, all filed and none on the route to
sign-off. #109 — every execution row is
written execution_engine: localex, including Tapis runs; the server does not read the column,
so nothing breaks today. #111 — the Runs
step prints 2 runs (1 input resources × 1 parameters); the count is right and only the printed
factor is wrong. #112 — every resource with
an empty URL shares the id d41d8cd98f00b204e9800998ecf8427e, the MD5 of the empty string. Every resource with an empty URL shares one id: the MD5 of the empty string #112 is the one to revisit if result registration ever starts writing resource rows.
Destination
TACC uses
ui-reactatmint.tacc.utexas.eduin place of the Lit UI, and the clientmanager signs off that the SOW's acceptance criterion (§8 — a model input's variable,
standard variable and unit configured in one form submission) is met.
That sign-off closes out the SOW. The map ends there.
Notes
Domain. MINT / SUBSIDE model catalog. See ADR-0002
for the React migration and ADR-0001
for the Hasura backend it sits on. Both have a Traps section — read it before debugging
anything that looks like a permissions or cache bug.
This map carries execution, not just decisions. The remaining work is deploy-and-verify,
so tickets do the thing rather than only decide it.
Contract. The SUBSIDE model-catalog UI SOW with UT Austin / TACC. Its period of
performance has expired (confirmed not to be a blocker) and the engagement is settled
financially, so the only remaining obligation is delivery and sign-off.
Contract number, terms, amounts, billing status and client contacts are deliberately not
recorded here — this repo is public. They live in the private metalearn wiki. Keep them
out of this map and its tickets.
Standing preferences for this effort
kubectlaccess.ui-reactreplaces Lit atmint.tacc.utexas.edu(decided; T1 de-risks it)./grillingand/domain-modelingon decision tickets.State at charting (2026-08-08). Last code commit 2026-07-20; ~3 weeks idle.
ui-reactiscontainerized and published, 21 routes ported, and has run only on Vercel previews and
localhost. OAuth2 has been exercised on previews and works — but previews use a dedicated
OAuth path for preview origins, so the round-trip at
mint.tacc.utexas.eduis still unproven.The app has never been driven against TACC's own Hasura and CKAN.
Decisions so far
Confirm SUBSIDE can live without the un-ported Lit screens —
none are needed; the flip is free. Most are already dead or stubbed in Lit, Analysis and
Emulators have no nav link, thread Summary turned out to be ported, and thread Visualize was
never functional in Lit either. Also settled the rollout: no parallel deploy (no users on
Lit at TACC), Vercel is the test surface, and Lit stays deployed-but-unrouted at the upgrade
as the only undo, removed after sign-off.
Tag helm-charts 9.0.0-beta.3 and bump the submodule pointer off beta.2 —
the chart is reachable again.
9.0.0-beta.3tagged upstream on3d3aca8, submodule bumpedin #82, merged to
developas200a0d7.Both confirmations held:
ui_react.enabled: falseby default, and the legacyuicomponent is untouched (values.yaml is +52/-0, no
ui*template in the diff,_helpers.tplappend-only). Three facts fell out —
ui_reactneeds an OAuth2 client distinct fromLit's (one callback URL per client, so reusing Lit's breaks the undo — sharpened
#73); its image tag is a monorepo SHA
the bump automation will silently skip, so it is hand-set; and it takes its own host
rather than a path prefix, making the flip a host reassignment.
Verify CKAN from a browser origin against ckan.tacc.utexas.edu — is CORS actually broken? —
fine in production, broken on Vercel. CKAN echoes
Access-Control-Allow-Originonly for*.tacc.utexas.edu(not the apex),localhostand127.0.0.1; every other CORS header issent to everyone, so it looks enabled when it is not.
mint.tacc.utexas.eduis allowlisted,so production needs nothing from TACC and nothing from us — which killed
#74 (the nginx proxy only serves the
origin that already works, and never touches a preview), closed unopened.
*.vercel.appgotno header — since fixed: Max added the two real Vercel origins to CKAN's nginx map as
anchored regexes and dropped the pre-existing
*.github.iowildcard, verified live on everyaction the app calls plus the preflight. So
#76 runs wholly on the Vercel preview as
originally written,
/datasets/*included, on the branch aliasmonorepo-git-develop-mosoriobs-projects.vercel.app— per-deployment preview URLs are refusedand cannot be allowlisted, since the hash changes every push. No public-suffix wildcard is left
in the allowlist, which closes the credentialed-access concern the first fix opened.
Commit .env.example and local-setup notes so ui-react runs for someone who is not Max —
a
.envfile cannot configure the dev server. That was the finding, and it made theticket's premise wrong.
index.htmlalways loadspublic/env-config.js, so theVITE_*fallbacks never run. The real fault was one line:
HASURA_ENDPOINTpointed inside thecluster, at
graphql.mint.local. It now points to TACC's public Hasura. A clone plusnpm installplusnpm run devnow shows real model catalog data, read-only, with nocluster access. I verified this live from a
localhost:3000origin. The ticket also added.env.example,npm run config:localand a README section, and deleted.env.development. It deletedMODEL_CATALOG_API, which no code used. That left#75 with a fixed list of 12 keys.
ENSEMBLE_MANAGER_APImust be set, or thread execution stops without an error. The code isin PR #83, merged to
developas45a6d7e.Check whether the OAuth2 redirect URI for mint.tacc.utexas.edu is already registered —
already registered; nothing to do at the IdP. The premise was wrong: Lit uses
/oauth2/callbacktoo (${window.location.origin}/oauth2/callback, hardcoded), so both appswant the identical redirect URI. The IdP is Tapis, tenant
portals— not Keycloak — andclient
minthascallback_urlexactlyhttps://mint.tacc.utexas.edu/oauth2/callback.Verified live, with negative controls, against
GET /v3/oauth2/authorize, which validatesredirect_uribefore login: the match is exact on scheme, host and path (http://isrejected), and no
mint-*react*client exists. Tapis really is one-callback-per-client, butthe Tag helm-charts 9.0.0-beta.3 and bump the submodule pointer off beta.2 #70 inference that
ui_reactneeds its own client does not follow — the two apps wantthe same URI and never serve the origin at once (Lit is unrouted). Decision: reuse
mint,so there is no IdP change at flip or rollback. The chart's own text argues the opposite and
is wrong for this rollout; it only hard-fails on an empty
client_id, so"mint"renders —flagged on #75. Two constraints landed on
#77: the host must be exactly
https://mint.tacc.utexas.edu, and Lit must be unrouted, never re-hosted — parked at asecond hostname it cannot log in, making it a rollback to a broken app. Also rules out a
staging host for
ui-reactbefore the flip.Settle the window.MINT_CONFIG values for the TACC deployment —
two values, not twelve. The deployed Lit app publishes TACC's real config at
mint.tacc.utexas.edu/config.js, so no value had to be guessed. Ten of the twelve keys needno statement: they derive from the component ingresses or inherit shared chart values TACC
already overrides (
google.maps.key,welcome_message). Onlyclient_id: "mint"anddata_catalog_api: "https://ckan.tacc.utexas.edu"must be set. Derivation is proven safefor Hasura and the Ensemble Manager rather than assumed — the Lit template and the
ui_reacthelper share the same
ingress.tlsidiom, and Lit's live output showshttps://for both, soTACC declares TLS and there is no mixed-content trap. CKAN is the exception:
ui_reactderivesit from
components.data_catalog, which TACC does not use (it usesexternal_services.ckan.url,read by the Lit template only), so the key is omitted and the app falls back to a compiled
default that is right by coincidence — hence the override. Verified live from the production
origin: Hasura and the Ensemble Manager both send
Access-Control-Allow-Origin: *, theEnsemble Manager's
/v1routes exist, and CKAN sets noX-Frame-Options, so the datasetsiframe will render. Two facts landed on
#76 —
ui-reactneeds no CKAN API key(Lit's
DATA_CATALOG_KEYhas no counterpart; all reads are anonymous GETs), andENSEMBLE_MANAGER_APIis now set, so thread execution is in scope for the smoke test. Threechart cleanups landed on #77: the dead
MODEL_CATALOG_APIkey, the wrong "must be a distinct client" text, and teachingui-react-config.yamlto readexternal_services.ckan.url. None block the flip.Ensemble Manager calls send no Authorization header — localStorage key mismatch —
fixed in PR #87, merged to
developas4f7debf.Both REST calls read the token by string literal from a key nothing writes, and spread the
header conditionally, so every call went out anonymous with no error. The symptom is now known
rather than guessed, from the Ensemble Manager source: against a
tapisengine, which iswhat TACC runs, submit returns 500 ("Unauthorized" escapes as a plain
Error) and logsreturn 401 — both read as server faults, not a missing credential. The probe could not
settle this because
fetchLoglooks the execution up before it reads the header, so anunknown id 404s either way. Both calls moved into
src/lib/ensemble-manager.tsso the headeris built once; the tests assert the outgoing header, not the storage key, and were checked
to fail against the old code. The fix exposed a second defect and did not resolve it:
#88 —
ui-reactposts to/executionEngines/localex, and the only such route is/executionEngines/tapis, so runsubmission 404s before auth matters. Submission is still broken at TACC; the log call is
fully fixed.
ui-react submits runs to /executionEngines/localex, a route that does not exist —
two faults, not one. The engine name was wrong for TACC, and the route was wrong for every
engine except Tapis:
server.tsmounts/executionsLocal,/executionsand/executionEnginesas three separate routers, and the last registers/tapisonly. So aconfig key alone would not have fixed it. The engine is now the config key
EXECUTION_ENGINE, andexecutionEnginePathmaps each backend to its real route — the samemap Lit makes in
mint-runs.ts:731— so the key is honest for all three engines. Thedefault is
localex, matching both the chart default for the Ensemble Manager and the Litfallback, so one unset value describes the same deployment in three places. The results step
needed nothing:
MintResultsreads the engine off the execution row, not from config, and itslocalex/wingsrewrite is dead becauseMintThreadnever passesmintConfig. Code is inPR #89, merged to
developas1d1ada9.One constraint landed on #77: the chart must
emit
EXECUTION_ENGINE: tapisfor TACC, derived the wayui-config.yamlderives Lit's — aprerequisite for run submission, not a cleanup. Both halves confirmed live on
#76: TACC's Ensemble Manager publishes
exactly the three separate mounts predicted here, and its Lit config sets
REACT_APP_EXECUTION_ENGINE = "tapis".Smoke-test ui-react against TACC's real Hasura and CKAN —
the §8 acceptance criterion passes, for a new input. Signed in as
mosorio@portals, a newmodel input took label + standard variable (
soil__porosity) + unit (m3 m-3) in onesubmission against TACC's live catalog. The form is genuinely flattened — one control for
standard variable and unit, which offers the units already used with the variable chosen. The
cmdk trap ADR-0002 warns about could not apply: this picker is plain buttons, not cmdk. Verified
anyway by hit test and a real mouse click. Run on
localhost:3000, not the Vercel preview —the preview is behind Vercel SSO, and
localhostis CKAN-allowlisted with a working OAuthclient, so it tests strictly more. Editing an existing input fails, and that is
#91, now blocking
#77: the junction upsert names a column this
repo's own metadata never exposes, so it is not TACC-specific, and it half-applies. A second,
minor fault rides along — a successful save renders stale until a manual reload. Thread
execution was carved out as #92 rather than
fired at the client's cluster unasked.
/modeling/*renders against real data; note the regionfilter defaults to California, where TACC has nothing — it reads as an empty deployment until
you switch to Texas. All test rows were removed; TACC's catalog is back to its prior state.
Editing an existing model input fails: the junction upsert names a column Hasura does not expose —
fixed and verified live; the write surface is a
graphql_enginequestion, not a TACC one.Client-only, so nothing to apply to TACC's Hasura.
SetConfigurationInputOptionalreplaces theupsert with
delete_by_pk+insert_onein one document — Hasura runs a mutation's rootfields in a single transaction, so the row is never left missing — and it now fires only when
is_optionalchanged, instead of for every row intoUpdate. Proven under a real Tapis tokenwith a zero-write probe: validation precedes execution, so the old document returns
validation-failed(the exact_PLACEHOLDERerror) and the new one reaches execution. Whynothing caught it:
generated/schema.graphqlis the admin schema, where the column isupdatable — codegen and all 760 offline tests validate against a schema the app never runs as.
Treat any role-sensitive write as unverified until it runs under a real token. Fault 2 fixed too:
the form refetches
GetConfigurationbeforeonSaved, so a save no longer renders stale.End-to-end on
simpleModelAnnotated— add, edit label, toggle Optional — all clean, and thecatalog was returned to its exact prior state. Code in
PR #93. One thing deliberately not fixed:
the save is still many mutations, not one transaction, so a mid-sequence failure still
half-applies. No step on this path can fail that way now; ticket it if TACC hits it.
Thread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field —
fixed and verified live: 3 of 40 annotated variables → 40 of 40. Client-only; nothing to
change at TACC.
findDatasetsno longer sends aqat all —packagesMatchingVariableskeepsthe packages whose resources carry the variable, as Lit does. The ticket's premise was
re-measured first and held. Three defects the ticket did not name, each real: resource
narrowing was mandatory, not cosmetic — a dataset matches because some of its resources carry
the variable, and one TACC package holds 35 resources, 1 of which does, so binding all of
them handed the model input files it cannot read;
datatypewas read off the first resource,which
MintDatasetsthen filters the list on; androws=1000was a silent cap, now paged.Two existing fixtures encoded the bug — they carried no annotation and passed only because
the old code matched prose. Code in PR #95,
CI green. Submit one thread run end to end against TACC's Ensemble Manager #92 is unblocked, with one constraint landed on it:
ext_bboxdrops any packagewith no spatial extent, and 11 of TACC's 33 annotated packages have none — Texas keeps 16 of
33, the California default keeps 0. Lit behaves the same, so it is not a regression, but a
region-scoped thread cannot see a third of the annotated catalog — now
#97. The same root cause survives
on the browse surface as #96, deliberately
not fixed here.
Missing dataset metadata must not mean an invisible dataset (decided 2026-08-09, no ticket
of its own to close — it scopes #97). The
four constraints on the Datasets step disagreed about what unknown means: the standard
variable drops it (right), the datatype offers a "Show N" link, dates keep it, and the
region dropped it silently — because
ext_bboxfilters on having a location, not onlocation. Region was also the only constraint on the server. The rule is now two rules:
unknown extent is shown and badged, as dates already do; known-but-outside is hidden behind the
counted link the datatype filter already has. Chosen over a single show-everything toggle,
which cannot say why a row was hidden, and over silently widening the results — the exact
failure Thread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field #94 had just demonstrated. Cheap only because Thread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field #94 made
findDatasetsread the wholecatalog, so dropping
ext_bboxcosts no request. One trap found while scoping it:packageSpatialCoveragereadscoordinates[0]and handles a bare Polygon only, while TACC alsoholds
FeatureCollection×5,Feature,PointandMultiPolygon— moving the filterclient-side without a real bbox extractor would drop those 8 without a word. Datasets step: show the datasets the region filter hides, instead of dropping them #97 blocks
#77: TACC must meet a correct Datasets
step, not an empty one. One thing Datasets step: show the datasets the region filter hides, instead of dropping them #97 does not yet say, seen live on Submit one thread run end to end against TACC's Ensemble Manager #92: Thread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field #94 made
findDatasetsread the whole catalog, so the region now narrows the Datasets step not atall — an Alaska package (
Bethel Elevation) was offered under a Texas framing. That is theopposite failure to the one Datasets step: show the datasets the region filter hides, instead of dropping them #97 was written for, and the same rule has to settle both.
Dataset search by variable name searches CKAN free text, not mint_standard_variables —
fixed and verified live:
groundwater48 → 12,corpus_nlp0 → 17. Client-only; nothing tochange at TACC. The measured premise held exactly. Substring-match the annotation, prose
dropped — chosen over keep-prose-and-badge, which leaves the count wrong and cannot fix
corpus_nlpat all (Solr splits the name on_before it matches), and over a typeahead onHasura's 668 standard variables, where only a small fraction are carried by any TACC dataset, so
a user picks a legitimate variable and gets a confident 0. Substring matching only ever offers
what exists, and it agrees with the Datasets step: show the datasets the region filter hides, instead of dropping them #97 rule: filtering by standard variable correctly drops a
dataset that carries no annotation. A second, independent fault rode along and hit both modes:
searchDatasetstook CKAN'srows=100default while TACC holds 215 packages, so the empty termlisted 100 of 215 and said nothing — now
searchAllPackages, which Thread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field #94 had already taught topage.
buildSearchQuerylost itsvariablesbranch entirely; it only ever built a query Solrcannot answer, and this was the second surface it broke. One ticket framing was wrong: this was
never a regression. Lit's own
/datasets/searchis dead at TACC — it POSTs todata_catalog_api + "/find_datasets"(ui/src/screens/datasets/actions.ts:377), the legacy MINTData Catalog route, never routed through
DataCatalogAdapter, sodata_catalog_type = "CKAN"does not redirect it; TACC answers 405 for both modes. So "match Lit" was not available as a
constraint. Code in PR #100, full suite green
at 813 tests; the 22 new assertions were checked to fail against the old code, on a path that had
no test file at all.
Thread wizard shows 18 of 175 model configurations: the model tree query accepts only sdm#Model —
no type belongs out of the model tree; all three queries drop the predicate. Verified live in
the signed-in app: the Models step now reports 175 where it reported 18, and HAND — TACC's
largest family, 44 configurations — goes 0 → 44. The ticket's premise was wrong.
typeclassifies a model; it does not say whether a row is one. The ETL only migrated
?id a sdm:Modeland stored the most specific subtype, so every row is a model by construction,and the 8
sd#Softwarerows carry the ontology superclass — MODFLOW ×5, ParFlow, an InSARworkflow, a particle filter. So
_neq sd#Software, the ticket's own candidate, would have hiddenreal models. An allowlist is worse and this was measured, not argued:
model-catalog-apikeeps one and it is already stale, serving 44 of 55 — #102,
ruled out of scope below. All three sub-questions answered the same way:
GetModelFamiliestoo(registration could not offer any subtype family, so no version could be registered under HAND),
and
RegionModelstoo (it shares the query, so a different rule would only make them disagree).Registration still writes
sdm#Modelfor a new family, which is correct — read and write aredifferent questions. Code in PR #101, 802
tests green, the 9 new assertions checked to fail against the old code;
graphql.tsregeneratedby codegen from the committed admin-schema snapshot rather than hand-edited, which is worth
remembering — codegen runs offline against
generated/schema.graphql, no admin secret needed.Submit one thread run end to end against TACC's Ensemble Manager #92 is unblocked. The fix also exposed the next wall in the same step, now
#103: the Variables step offers 668
indicators where 147 can find a model.
Thread execution blocks the flip;
ui-reactmust run a thread before TACC gets the URL(decided 2026-08-09 by Max, no ticket of its own to close — it scopes
#104). The SOW's §8 criterion is already
met (#76), so Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 is not required by the
contract's letter. It is required anyway: Lit runs threads at TACC today, and flipping the host
to an app that cannot would hand the client a regression, whatever §8 says. Chosen over shipping
on §8 alone, and over asking the client — asking would invite a "no" to a question we would not
act on. Consequence: Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 is now the largest item left on the map, and unlike Editing an existing model input fails: the junction upsert names a column Hasura does not expose #91/Thread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field #94/Dataset search by variable name searches CKAN free text, not mint_standard_variables #96/Thread wizard shows 18 of 175 model configurations: the model tree query accepts only sdm#Model #98
it is a data path to build, not a query to correct. Sequence it before
#97,
#99 and
#103, which are all smaller.
Thread wizard dead-ends at Parameters: threadExecutionData is never loaded —
the pipeline now loads from Hasura, and the ticket's premise was half the fault.
threadExecutionDatawasuseState(null)that nothing loaded, as written — butDatasetsSteppersisted nothing either, callingUpdateThreadDatawith emptydataand
modelIOagainst a mutation that deletes every binding first. So Continue destroyedbindings, and Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104's own measurement that the assignment survived a reload is retracted.
That mattered: loading the pipeline alone would have changed nothing, because there was
never a binding to read back.
GetThreadExecution+threadExecutionFromGQLare portedfrom Lit's
get.graphqlandgraphql_adapter.ts; Datasets and Parameters now write;executions load and the summary polls while runs are in flight. Three defects found in
the wiring, all where the React port had drifted from Lit: resource ids were a djb2 hash
where Lit writes MD5 of the URL — and since this ticket is what starts writing those rows,
shipping it would have duplicated every file TACC already had;
run_progressis afraction, drawn as a percentage, so a finished run rendered as a 1% sliver; and
total_runsdropped the input-resource factor, where TACC's own summaries keep it. Verified live three
ways — anonymously for the whole
thread_modelssubtree and the executions query, under areal Tapis token for
thread.thread_dataanddataslice.dataset(bothuser-only), and azero-write probe of the parameters mutation that reached
constraint-violationrather thanvalidation-failed. The running app rendersRuns ✓ Completefrom TACC where every stepread
Pendingbefore. Code in PR #105,852 tests green.
generated/schema.graphqlis the admin schema — the role trap Editing an existing model input fails: the junction upsert names a column Hasura does not expose #91found for writes applies to reads too: anonymous sees neither
thread_data, northread.events/permissions, nor thedatasettable. Submit one thread run end to end against TACC's Ensemble Manager #92 is unblocked, and carriesthe one thing Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 could not prove: the two write round-trips through the UI.
Submit one thread run end to end against TACC's Ensemble Manager —
a run went from
ui-reactto TACC's cluster and finished. ModelSUBSIDE Allocation Specification,POST /v1/executionEngines/tapis→ 202, two real Tapis jobs, one ofwhich ran to completion and wrote a full output set. That is the first
ui-reactrun toproduce output at TACC, and it retires the "unproven path" risk this ticket existed for.
Of the four checks, two pass and two fail. Submit returns 2xx, and the Runs step polls
and tracks status live with no 401 — so Ensemble Manager calls send no Authorization header — localStorage key mismatch #85 and ui-react submits runs to /executionEngines/localex, a route that does not exist #88 both hold under a real submission.
The execution row says
execution_engine: localex(#109): a hardcoded literal in
ExecutionCreation, harmless today because the server readsprefs.execution_engineinstead, wrong for every consumer of the column. And the Results step never shows any
output (#110) — two independent
causes, which matters because fixing either alone changes nothing:
MintResultsguards ononPublishResults, a propMintThreadnever passes, so Fetch results makes no request atall; and the route it should call returns 500 wrapping a 403 from Tapis or CKAN. Results step never shows any output: the Fetch results button is not wired, and the publish route returns 500 #110
blocks Flip mint.tacc.utexas.edu to ui-react #77. The wall-per-fix pattern has finally run out of walls in this half — the
wizard now walks Framing → Results without a new blocker appearing, and the two failures
are at the far end, not one step in. Two workarounds confirmed as sound: Models step Continue does nothing once a thread has execution data: delete_thread_model is refused, and the error is swallowed #107 only bites
a thread that already holds bindings, so a fresh thread clears the Models step, and Variables step cannot save any indicator: it writes a standard-variable URI into a column that references the legacy variable table #106 is
steered around by leaving the indicator empty. Both run failures were data, not code —
an
.m4afed to a JSON-reading model, then a CKAN resource holding invalid JSON (a rawnewline at byte 25). Both were diagnosed from the Ensemble Manager log endpoint, which
works: read the log before blaming the client.
Datasets step: show the datasets the region filter hides, instead of dropping them —
two faults, opposite in direction, and the ticket named only one.
ext_bboxhid the 11 of33 annotated packages with no extent, as measured — but
MintThreadalso never passed theregion to
DatasetsStepat all, so what did arrive was not narrowed either. One rule had tosettle both, and does: unknown extent is shown and badged
! no location; known-but-outside ishidden behind a counted link.
findDatasetsnow drops nothing for the region — it labels eachdataset
region_match: inside | outside | unknownand the UI decides. Verified live at TACC:Texas 29 offered (18 in-region + 11 badged) and 4 behind the link, where
ext_bboxserved16; California — the default framing — 11 badged rows where it showed 0, which retires the
"reads as an empty deployment" note.
ext_bboxis not a server-side version of the sametest: ckanext-spatial indexes bare geometries only, so it also drops a
FeatureorFeatureCollection, and TACC has two of those squarely inside Texas — hence 16 → 18, not16 → 16. The extractor replaced two partial readers, not one: the region map's
calculateBoundingBoxmissedFeature.geometryandFeatureCollection.featurestoo, solib/geo/bbox.tsnow serves both surfaces and the region picker gained the coverage for free.GetThreadselectsregion { geometries }, confirmed readable under anonymous. Code inPR #113, 883 tests green; the
Feature/FeatureCollection/Point assertions were checked to fail against both old readers.
Two traps left standing, neither blocking: a bounding box is not the region — Alaska crosses
the antimeridian, so its box spans nearly the whole globe and almost nothing reads as outside it
(Lit is the same; it does not bite Texas) — and the browse surfaces still carry the old fault,
now #114.
Deleting a problem statement, task or thread leaves the row behind and reports success —
the permission was unsatisfiable by construction, so this is the first fix on the map that
needs a schema migration. The
userrole may delete one of these rows only while it stillcarries a CREATE provenance event; the provenance FKs were
ON DELETE RESTRICT. Delete theprovenance first and the row loses its own delete permission (0 rows, reported as success);
delete the row first and Postgres refuses it. Both orders were run against a live database
and neither works, which kills the ticket's own first option and rules out any client-only
fix. The ticket named one failed root field; all eleven failed — every child on the tree is
gated through the same provenance, so only the provenance and permission rows were ever really
deleted, which is exactly the state Submit one thread run end to end against TACC's Ensemble Manager #92's orphan was found in. Six FKs become
ON DELETE CASCADEin graphql_engine PR #14 —metadata unchanged, structural FKs untouched — and the client deletes bottom-up, never
touching provenance, in PR #115 with
assertDeletedto stop a zero-row delete rendering as success. A second defect had to be fixedwith it: the port dropped Lit's
dataslice/thread_datadeletes, harmless while themutation was a no-op but a hard FK error once Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 started writing those rows. Verified on the
migrated dev cluster under Hasura's own compiled predicates, in a rolled-back transaction; the
Submit one thread run end to end against TACC's Ensemble Manager #92 orphan is cleared. Two things outlive this ticket. Lit's delete mutations are
byte-identical, so delete is broken at TACC today — which is why Deleting a problem statement, task or thread leaves the row behind and reports success #99 no longer blocks
#77 (decided by Max, 2026-08-09, on the
same rule Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 set: a defect blocks the flip only if moving off Lit is a regression). And the
chart's Hasura migration job is a
post-installhook only, sohelm upgradenever appliesmigrations — TACC needs the six-FK change run by hand, after the flip, preferably as a surgical
run_sqlrather thanhasura migrate apply, whose state at TACC is unverified.The indicator is a standard variable, and the Variables step does not block the flip
(decided 2026-08-10 by Max, no ticket of its own to close — it scopes
#106 and
#103). Two calls, and the second one moves
the critical path. Storage:
thread.response_variable_idwill hold a standard-variable URI.The four FKs on
thread/taskrepoint frompublic.variabletomodelcatalog_standard_variable, in agraphql_enginemigration on the Deleting a problem statement, task or thread leaves the row behind and reports success #99 pattern — FKs change,metadata does not, because the relationships are
foreign_key_constraint_onand follow theconstraint. Chosen over writing the label, which covers 129 of the 147 producible output
variables today and only decays:
etl/load.pywritesmodelcatalog_*tables only, sopublic.variablehas had no writer since the DYNAMO migration — treat it as frozen and buildno list from it. Data cost at TACC is one row; the unmeasurable risk is ISI's, so the
migration must map by label and null what does not map, never drop rows. Scope: Variables step cannot save any indicator: it writes a standard-variable URI into a column that references the legacy variable table #106 and Variables step offers 668 indicators; only 147 can find a model #103
both stop blocking #77, on the same rule Deleting a problem statement, task or thread leaves the row behind and reports success #99
set — Lit's Variables step is dead at TACC too, so moving off Lit loses nothing.
ui/src/screens/variables/actions.ts:31hardcodesis_indicator: falseon every entry andrenderResponseVariablesfilters on exactly that flag, so Lit's select renders zero options;live, 1 of 165 threads and 0 of 55 tasks carry a response variable and none carry a
driving variable. ui-react's step is skippable, so it traps a user who picks but does not stop
one who does not. Variables step cannot save any indicator: it writes a standard-variable URI into a column that references the legacy variable table #106 now blocks Variables step offers 668 indicators; only 147 can find a model #103, and Flip mint.tacc.utexas.edu to ui-react #77 is down to two blockers,
#107 and
#110 —
twonow one, Results step never shows any output: the Fetch results button is not wired, and the publish route returns 500 #110; Models step Continue does nothing once a thread has execution data: delete_thread_model is refused, and the error is swallowed #107 isfixed, see below. One consequence to file when the
migration lands: the Ensemble Manager's
POST /v1/problemStatements/*/subtasksvalidates theseids against
variable, so it will reject every URI — pre-existing, and ui-react does not use thatroute.
Apply the modeling provenance cascade migration to TACC's Hasura —
delete works at TACC, and the migration alone was never enough. The schema change was applied
on 2026-08-10 (six FKs read
confdeltype = c, no drift, one 100-dayidle in transactionsessionhad to be terminated first — Set idle_in_transaction_session_timeout on TACC's Postgres #119, runbook docs: runbook for the TACC cascade migration #118). Step 4, the round trip under a real Tapis token,
is now done: a throwaway problem statement created, deleted and gone after a reload, with
delete_threadanddelete_taskataffected_rows 1anddelete_problem_statement_by_pkreturning a row — the value Deleting a problem statement, task or thread leaves the row behind and reports success #99 could never get. The successful delete is itself the proof the
FKs are CASCADE, since under the old
RESTRICTit raises a foreign-key violation; that is themigration verified from the app, not from
confdeltype. Confirmed asanonymousafterwards,not just in the app — an orphan is invisible to its owner and visible to
anonymous, which is howDeleting a problem statement, task or thread leaves the row behind and reports success #99 hid. Run at
localhost:3000against TACC's production Hasura, not at the host, and theliteral test was not available:
mint.tacc.utexas.edustill serves Lit, whose delete removes theCREATE provenance as root field 6 before the row delete in the same transaction, so it fails for
a reason unrelated to the migration — a false negative. Origin is not part of this round trip (same
endpoint, same database, same
userrole); what localhost cannot prove is the deployment, which isFlip mint.tacc.utexas.edu to ui-react #77's job, and a re-confirm-after-flip constraint is landed there. Two things outlive this
ticket. The migration alone does not fix delete — the client fix fix(ui-react): delete a problem statement, task or thread bottom-up (#99) #115 is required, and Lit
stays broken, so a rollback to Lit is a rollback to silent orphaning. And a stale dev server from
another worktree held port 3000, so two runs exercised day-old code that looked right and produced
a confident wrong diagnosis; the request body gave it away, not the UI. Start dev servers with
--strictPort, and read the request body before believing a live verification. That accident wasa clean negative control — the old client against the migrated database reproduced Deleting a problem statement, task or thread leaves the row behind and reports success #99 exactly,
HTTP 200 with no
errorskey — and it left two orphan rows at TACC that no client can delete,now #120.
Models step Continue does nothing once a thread has execution data —
fixed and verified live; the step now saves by diff, and Lit's answer was the wrong one to copy.
Both faults held as written: the mutation deleted every
thread_modelrow for the thread againstfour
ON DELETE RESTRICTchildren, andModelsStephad nocatch, so the refusal was silent.diffThreadModelskeeps a still-selected row and its id, deletes only deselected rows withtheir four children, and inserts only what is new — so an unchanged Continue writes nothing at
all. Keeping the id is the whole point: the children hang off
thread_model.id, so nodelete-and-reinsert can preserve them. Lit clears the wall by deleting every child row for the
thread on every save, which destroys the Datasets and Parameters work each time — not copied.
Verified on thread
x4gbmq00msmbp9ibunder a real Tapis token, reading request bodies: unchangedContinue sent zero GraphQL requests and advanced the step; adding a model sent
removedIds: []with all five deletes at
affected_rows 0; removing it sentmodels: []anddelete_thread_modelat
affected_rows 1; and the kept row's id, data binding andtotal_runs 2were untouched. TACC isback to its exact prior state. Three things outlive it.
insert_<table>(objects: [])isaccepted under the
userrole — Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 implied it, this measured it. 21 of TACC's 109thread_modelrows carry nomodelcatalog_configuration_id, and the step deliberately leavesthem alone: deleting one would hit the same RESTRICT wall on exactly the legacy threads that predate
ui-react. And removing a model still discards its runs without warning — Lit does the same for every
model on every save, so it is not a regression; ticket it if TACC hits it. Code in
PR #121, 921 tests green, the 4 new assertions
checked to fail against the old code. Flip mint.tacc.utexas.edu to ui-react #77 is down to one blocker,
#110.
Not yet specified
#76 drove the signed-in app against TACC's
real catalog and found one blocker, #91,
plus one cosmetic fault. The reachability half is gone
(#75); the registration half is gone
(#73); the thread-execution half is
not closing as expected.
4f7debfand1d1ada9fixed auth and routing, but the attempt at#92 never reached the submit button: the
Datasets step could not be satisfied, so Runs stayed locked. That exposed
#94 — now fixed, 40 of 40 annotated
variables. Submit one thread run end to end against TACC's Ensemble Manager #92 was retried on that fix and stopped one step earlier, at the Models step —
#98, now fixed: 18 of 175 configurations
→ 175, and HAND 0 → 44. Submit one thread run end to end against TACC's Ensemble Manager #92 is unblocked. The pattern is worth naming: each fix on this half
reveals the next wall one step further in, and each wall was invisible until something upstream of
it worked. Thread wizard shows 18 of 175 model configurations: the model tree query accepts only sdm#Model #98 was not the last one — resolving it exposed the next wall in the same step,
now #103: the Models step also filters by
the thread's indicator, and the Variables step picker offers 668 standard variables where
only 147 are produced by any configuration — so 78% of legitimate choices dead-end with a
bare
No models found.That is the exact failure mode Dataset search by variable name searches CKAN free text, not mint_standard_variables #96 rejected by name for dataset search, sothe map has already decided against it once. The right list is not missing:
useFacetOptionsbuilds it for the
/modelsbrowse facet and gets exactly the 147. Variables step offers 668 indicators; only 147 can find a model #103 blocks Flip mint.tacc.utexas.edu to ui-react #77. Submit one thread run end to end against TACC's Ensemble Manager #92 is notblocked by it — it can steer around it: pick an indicator from the 147, and
RECHARGE_VOLUME_FLUX,DRAWDOWNorSTORAGE_VOLUMEeach land on all 44 HAND configurations.(34 of the 175 leaves produce no output standard variable at all, so no indicator reaches them.)
Submit one thread run end to end against TACC's Ensemble Manager #92 also carries a constraint from Thread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field #94:
ext_bboxhides the third of TACC'sannotated catalog that has no spatial extent, so pick the region deliberately.
The write surface is now measured on the edit path too —
#91 fixed the one blocker it held and the
cosmetic fault with it. What is left in fog is narrow: whether the deployment presents the
right origin at the real host (that is
#77's job), and what the rest of the
userrole's write permissions do against what the client sends — outputs, parameters,authors and regions have never been driven under a token. The delete path is now measured and
it is broken — #99: deleting a problem
statement, a task or a thread removes the row's provenance before the row, and the
userdelete permission depends on that provenance, so the delete matches 0 rows, returns no error,
and orphans the row — invisible to its owner, still visible to
anonymous. Same class as Editing an existing model input fails: the junction upsert names a column Hasura does not expose #91: awrite that is valid against the admin schema and fails under the
userrole.It blocks Flip mint.tacc.utexas.edu to ui-react #77.Now fixed, and it no longer blocks Flip mint.tacc.utexas.edu to ui-react #77 — see Decisions so far: the permission was
unsatisfiable by construction, so the fix is a schema migration, and Lit is identically
broken, so the flip loses nothing. On
the rate to expect — the authenticated path has now produced six defects (Ensemble Manager calls send no Authorization header — localStorage key mismatch #85, ui-react submits runs to /executionEngines/localex, a route that does not exist #88, Editing an existing model input fails: the junction upsert names a column Hasura does not expose #91, Thread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field #94,
Thread wizard shows 18 of 175 model configurations: the model tree query accepts only sdm#Model #98, Deleting a problem statement, task or thread leaves the row behind and reports success #99) where the anonymous pass produced none, and each one was found only by walking one
step further. Budget for another round after the flip. Seven now, and the seventh is a
different animal — #104. The third
attempt at #92 cleared the Datasets step
for the first time (
1 / 1 inputs, and the binding persists across a reload), then dead-endedone step further in, at Parameters. This wall is not a wrong query.
threadExecutionDatainMintThread.tsxstartsnull, no query ever loads it, and both setters bail out onnull— so Parameters, Runs and Results are fed an empty object and are, in effect, stubs. Parameters
shows
Please select model(s) first.with no Continue button; Runs and Results never unlock;and
datasetsCompletereads the same dead field, which is why a satisfied Datasets step stillreads
Pending. So the earlier wall-per-fix pattern stops describing this: Ensemble Manager calls send no Authorization header — localStorage key mismatch #85 and ui-react submits runs to /executionEngines/localex, a route that does not exist #88fixed the submit call, but the wizard has never been able to reach it, and no further
one-query fix gets there. Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 blocks Submit one thread run end to end against TACC's Ensemble Manager #92 and Flip mint.tacc.utexas.edu to ui-react #77. Two corrections ride along.
docs/adr/0002-...:104lists parameters/runs/results as ported and merged — it is wrong forall three, so stop trusting that line as a porting inventory. And the flip question changes
shape: this is no longer "does a defect block sign-off" but "does TACC lose working thread
execution by moving off Lit" — a scope call for the client, not a bug to fix on the way.
What Submit one thread run end to end against TACC's Ensemble Manager #92 did confirm is real: Thread wizard shows 18 of 175 model configurations: the model tree query accepts only sdm#Model #98 holds (175 configurations), Thread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field #94 holds (both annotated DEM
packages offered), and the whole Framing → Datasets route is clean at TACC.
Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 is now built and the wall-per-fix pattern finally broke — but not for the reason
expected. It was two stubs, not one: the read path and the Datasets write path, and the
earlier reading that the binding persisted was wrong. What is left in this patch of fog is
narrower than it has been: the two write round-trips through the UI, which Submit one thread run end to end against TACC's Ensemble Manager #92 now owns.
The rate to expect has not changed — the authenticated path has produced seven defects, and
Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 alone added three more found only by driving the documents at TACC rather than reading
them. Budget for another round after the flip. One thing Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104 settled that outlives it:
generated/schema.graphqlis the admin schema, and theanonymousrole at TACC cannotsee
thread_data,thread.events,thread.permissionsor thedatasettable — so"it typechecks" and "codegen is happy" say nothing about the role the app runs as. Probe
live, per role, before believing a document.
This patch is now walked to its end. #92
ran a real job on TACC's cluster and read its log, so the two write round-trips it owned are
proven and the submit path is no longer an unknown. What is left here is one wall, at the
far end: #110, the Results step,
which blocks Flip mint.tacc.utexas.edu to ui-react #77. The defect rate held — the authenticated path has now produced ten
(Ensemble Manager calls send no Authorization header — localStorage key mismatch #85, ui-react submits runs to /executionEngines/localex, a route that does not exist #88, Editing an existing model input fails: the junction upsert names a column Hasura does not expose #91, Thread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field #94, Thread wizard shows 18 of 175 model configurations: the model tree query accepts only sdm#Model #98, Deleting a problem statement, task or thread leaves the row behind and reports success #99, Thread wizard dead-ends at Parameters: threadExecutionData is never loaded #104, Variables step cannot save any indicator: it writes a standard-variable URI into a column that references the legacy variable table #106, Models step Continue does nothing once a thread has execution data: delete_thread_model is refused, and the error is swallowed #107, Results step never shows any output: the Fetch results button is not wired, and the publish route returns 500 #110), and every one came from walking one
step further rather than reading code. But the shape changed: this pass found no new wall
in the middle of the wizard, only at its exit. Budget for another round after the flip, and
expect it to be about what the app does with results, not about whether it can reach them.
One habit worth keeping from this pass: both run failures were bad data, and the Ensemble
Manager log endpoint named the cause exactly (
Invalid control character ... char 25).Read the log before suspecting the client.
The model-tree read surface has the same fault in two more places.Settled on#98: all three queries fixed together, and
a
grepoverui-reactconfirms they were the only ontology-type filters in the app — so theopen half of this patch ("does any other query filter this way") is answered: none do.
still unmeasured. Thread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field #94 was not a Hasura fault; it was the data catalog half, which no ticket
had driven under real annotations. The first look past the thread wizard found the same bug again
on the search surface, now fixed (#96):
groundwater48 → 12,corpus_nlp0 → 17. The pattern held — it is every CKAN read thatassumes an indexed field, not one bad call site.
What is still unmeasured isThis patch has now graduated —/datasets/browseand
/regions/:id/datasets.#114, filed while resolving
#97 and measured, not suspected: for Texas
/regions/:id/datasetslists 50 of the 88ext_bboxreturns, out of 215 packages, and the104 with no extent can never appear. Both faults Dataset search by variable name searches CKAN free text, not mint_standard_variables #96 named are live there — the
ext_bboxdrop and the silent row cap (
RegionDatasets.tsx:23,rows: 50with a box,rows: 20without)— and both now have a fix to copy rather than to invent: Datasets step: show the datasets the region filter hides, instead of dropping them #97's rule and
lib/geo/bbox.ts, andThread datasets: ui-react searches CKAN free text instead of the mint_standard_variables field #94's
searchAllPackages. Region datasets page lists 50 of 88, and hides every dataset with no extent #114 does not block Flip mint.tacc.utexas.edu to ui-react #77, on the judgement that a browse page isnot the first thing TACC meets in a thread; re-decide it if they open the regions pages first.
/datasets/browserides along on Region datasets page lists 50 of 88, and hides every dataset with no extent #114 and is still unmeasured. Keep the habit: check what asurface truncates before checking what it filters.
resolving #71 — the endpoint is
https://graphql.mint.tacc.utexas.edu/v1/graphql, it sendsAccess-Control-Allow-Origin: *(so Hasura is reachable from any origin, Vercel included — the CKAN allowlist is not
mirrored here), and
anonymouscan readmodelcatalog_standard_variableandmodelcatalog_unit, unlike ISI. But*_aggregateis absent foranonymous, same as ISI,so there are still no server-side facet counts. The read half is now settled — the
anonymous smoke test on #76 drove
/models,/variablesand theOutput variablefacet against TACC with no login and found nogap. ADR-0001's warning about
anonymousandmodelcatalog_standard_variabledoes not applyto TACC. The write surface is now partly measured too, and the answer was not a TACC gap:
#76 wrote a dataset specification, a
variable presentation and a junction row under a Tapis token, and deleted all three. The one
write that failed, #91, failed from this
repo's metadata, not TACC's — so stop framing the write surface as a TACC unknown and read it
as a
graphql_enginequestion. Now fixed, client-side, with no metadata change, and theedit path verified live end to end. What stays unaudited is the rest of the
userrole's writepermissions against what the client sends.
assumes.~~ Settled on #76: TACC holds
668 standard variables and 129 units, counted by paging since
anonymoushas no*_aggregate. Both are well above the assumption.Whether anyone but Max can run the authenticated app outside production.Fixed. Tapisclient
mint-localhost-3000now serveshttp://localhost:3000/oauth2/callbackand is therepo's default
AUTH_CLIENT_ID;mint-localkeepshttp://mint.local/oauth2/callbackfor thedev cluster. Clients are named after the origin they serve, since Tapis allows one callback per
client. A real login on
localhostsucceeded — and immediately exposed#85, a silent auth defect no amount of
anonymous testing could have found — which in turn exposed
#88. One real login has now produced two
defects; treat that as the rate to expect from the authenticated walkthrough.
Out of scope
models-compare/models-calibrate/models-cromo, thread Visualize, and the 3,256-LOCmodel-view. (Thread Summary was on this list in error — it is ported.) Never in the SOW,whose scope is the config workflow and the flattened form.
T1 confirmed SUBSIDE can live without
them; it does not port them.
ui/entirely. Downstream of the above, and a separate effort.mint.isi.educutover. Different host, different stakeholders, not this contract.model/model_io/model_parametertables, Fusekireferences in docker-compose and CI, absent row-level security on
modelcatalog_*.feature, not migration work.
model-catalog-api—#102:
/modelsreturns 44 of TACC's 55software from a stale subtype allowlist, and four
custom-handlers.tshandlers are cut harderstill, to 27. Real and measured, surfaced while resolving
#98. Out of scope because nothing on the path
to sign-off touches it:
ui-reacttalks to Hasura directly and never calls that API (Commit .env.example and local-setup notes so ui-react runs for someone who is not Max #72 deletedthe dead
MODEL_CATALOG_APIkey), and the only consumer is the Lit UI, which stays unrouted andis being retired. Filed so it is not lost.
#108: the Ensemble Manager's Tapis path
JSON-parses
has_component_location, so a model whose component is alocalexZIP (HAND) failswith an opaque 500. Surfaced on the first attempt at
#92 and proved by negative control — the
same 500, byte for byte, with the input dataset swapped. Out of scope because it is
pre-existing server behaviour, Lit hits it identically, and nothing on the path to sign-off
needs a ZIP component: Submit one thread run end to end against TACC's Ensemble Manager #92 finished by choosing a Tapis-backed model instead. It does not
block Flip mint.tacc.utexas.edu to ui-react #77.
sign-off. #109 — every execution row is
written
execution_engine: localex, including Tapis runs; the server does not read the column,so nothing breaks today. #111 — the Runs
step prints
2 runs (1 input resources × 1 parameters); the count is right and only the printedfactor is wrong. #112 — every resource with
an empty URL shares the id
d41d8cd98f00b204e9800998ecf8427e, the MD5 of the empty string.Every resource with an empty URL shares one id: the MD5 of the empty string #112 is the one to revisit if result registration ever starts writing resource rows.