Live API verification notes¶
Empirical findings from running midas-nx v0.8.0 against real MIDAS NX Open
API sessions (one Gen NX session, one Civil NX session, both connected via
the live cloud relay at moa-engineers.midasit.com, not mocked). This is
not sourced from the vendored manual — everything else in this repo is
typed strictly from docs/manual/*.md and treated as the single source of
truth; this file exists precisely because these findings are not in the
manual and shouldn't be silently baked into the SDK's typed contracts
(PRODUCTS, METHODS) without independent reproduction. See the caveat at
the bottom before acting on anything here.
Date: 2026-07-15. One MIDASIT account, one Gen NX process, one Civil NX
process, both freshly reset via /doc/new before testing.
Method¶
- Read-only smoke test: every
DbResourcesubclass acrossdb/,design/,post/whoseMETHODSincludesGETand whosePRODUCTSincludes the product under test got a liveGETcall against a blank new document. No fixtures needed — a blank model returns an empty result for every valid endpoint (confirmed shape:{"message": ""}for zero-row tables,{"<KEY>": {}}for zero-row/db/*tables), so a non-2xx response reliably signals a real problem rather than "no data." - Write round-trip (Gen only): built a minimal model — 1 material
(
MATL, concrete C24), 1 section (SECT, 600×600), 2 nodes, 1 beam element, 1 fixed support — then exercised representative endpoints from each design-code chapter (ch24db/design.py, ch25steel_kds.py, ch26rc_kds/setup.py, ch27src_aiksrc2k.py) against it.
Read-only results¶
| Product | GET-capable & product-compatible classes tested | OK | Failed (all 404) |
|---|---|---|---|
| Gen | 253 | 233 (92%) | 20 |
| Civil | 293 | 273 (93%) | 20 |
Failure breakdown¶
Hyper-S (-M1) endpoints — 13, fail under Gen.
⚠️ Corrected 2026-07-26. The original text here said these were "absent from the Civil target set entirely (their
PRODUCTSis already{"gen"}-only in the SDK)" and explained the Gen 404s as the session not running in Hyper-S solver mode. Both halves were wrong. TheirPRODUCTSwas{"gen", "civil"}, so they were in the Civil target set, and re-running the sweep shows all 13 answering under Civil. Hyper-S is the solver MIDASIT introduced with Civil NX — it is a Civil NX feature, not a Gen mode — so the Gen 404s are correct behaviour and the SDK'sPRODUCTSwas the thing that was wrong. Fixed viaHYPER_S_ONLYindb/base.py. See the 2026-07-26 section below. The claim was never checked against a Civil run; it was inferred, and the inference was wrong in a way that a one-linesorted(cls.PRODUCTS)would have caught.
/db/ACTL-M1,/db/BCGA-M1,/db/BCGD-M1,/db/EIGV-M1,/db/HHCT-M1,/db/NLCT-M1,/db/NLNK-M1,/db/STCT-M1,/db/THGC-M1,/db/THIS-M1,/db/THOO-M1,/db/POGD-M1,/db/POLC-M1
Failed under Gen, succeeded under Civil (7) — current SDK PRODUCTS
says {"gen", "civil"} for all of these (no restriction documented in the
manual), but this session's evidence points to Civil-only in practice:
midas_nx.db.construction_stage.CamberConstructionStage(/db/CMCS)midas_nx.db.design.RebarCheckInput(/db/RCHK)midas_nx.db.misc_loads.PreCompositeSection(/db/PLCB)midas_nx.db.misc_loads.WaveLoad(/db/WVLD)midas_nx.db.project.Span(/db/SPAN)midas_nx.db.properties.section.EffectiveWidthScaleFactor(/db/EWSF)midas_nx.db.properties.section.SectionStressPoints(/db/STRPSSM)
Failed under Civil, succeeded under Gen (20) — same situation, opposite direction; evidence points to Gen-only in practice:
midas_nx.db.boundary.DiaphragmDisconnect(/db/DRLS)midas_nx.db.boundary.SeismicDeviceHystereticIsolator(/db/SDHY)midas_nx.db.boundary.SeismicDeviceIsolator(/db/SDIS)midas_nx.db.design.BeamRebar(/db/REBB)midas_nx.db.design.BraceRebar(/db/REBR)midas_nx.db.design.WallRebar(/db/REBW)midas_nx.db.project.Story(/db/STOR)midas_nx.db.static_loads.SoilProperty(/db/POSP)midas_nx.db.static_loads.StaticEarthPressure(/db/EPST)midas_nx.db.static_loads.StaticSeismicLoad(/db/SSEIS)midas_nx.db.static_loads.StaticWindLoad(/db/SWIND)midas_nx.design.rc_kds.rebar.ModifyBeamRebarData(/DESIGN/RC/KDS-41-20-2022/REBB)midas_nx.design.rc_kds.rebar.ModifyBraceRebarData(/DESIGN/RC/KDS-41-20-2022/REBR)midas_nx.design.rc_kds.rebar.ModifyColumnRebarData(/DESIGN/RC/KDS-41-20-2022/REBC)midas_nx.design.rc_kds.rebar.ModifyWallRebarData(/DESIGN/RC/KDS-41-20-2022/REBW)midas_nx.design.rc_kds.rebar.TorsionReductionFactor(/DESIGN/RC/KDS-41-20-2022/TRFT)midas_nx.design.rc_kds.setup.ModifyConcreteMaterial(/DESIGN/RC/KDS-41-20-2022/MATD)midas_nx.design.rc_kds.setup.UndergroundLoadCombinationType(/DESIGN/RC/KDS-41-20-2022/ULCT)midas_nx.design.src_aiksrc2k.SrcModifyMaterial(/DESIGN/SRC/AIK-SRC2K/MATD)midas_nx.design.steel_kds.UndergroundLoadCombinationType(/DESIGN/STEEL/KDS-41-30-2022/ULCT)
All URL paths above were double-checked character-for-character against the manual's own "Input URI" for each endpoint — none of these are SDK transcription bugs. The URLs are right; the routes just weren't reachable from the product tested against.
Write round-trip results (Gen)¶
- Model build (
MATL,SECT,NODE,ELEM,CONS): all 5 calls returned exactly the documented response shape, verified byte-for-byte against what was sent. - ch24–27 config-singleton
PUTendpoints (RcDesignCode,SteelDesignCode,SteelDesignCodeOption,ConcreteDesignCodeOption,SrcDesignCode,SrcDesignCodeOption): all succeeded, response echoed the payload under the documented top-level key (DCON,DSTL,DCO,DCORC,DSRC,SRCDCO). - ch24
DesignMemberAssignment.create(registers element 1 as design member 1): succeeded. - Operational nuance, not a bug: once a design member is registered,
the server auto-seeds a default per-member parameter record (e.g.
LENG) for that member ID in every currently-selected design-code namespace. Calling.create()(POST) on that ID afterward returns{"error": {"message": "Key Already Exist"}}inside a 200 response (not an exception —MidasClient.request()only raises on non-2xx status, so callers must check for an"error"key even on success). Calling.update()(PUT) instead succeeds and a subsequentGETconfirms the value round-trips exactly. Confirmed forsteel_kds.UnbracedLength,rc_kds.setup.UnbracedLength, andsrc_aiksrc2k.SrcUnbracedLength, all targeting the same RC element — this also confirms the server does real semantic validation rather than blindly persisting writes (steel/SRC-code member parameters were still accepted on a plain-concrete element in this test, so no rejection was observed there, but the "already exists" behavior itself is a genuine cross-code-namespace side effect worth knowing about).
Extended verification: Civil-only chapters + full analyze→results round-trip¶
Two more sessions were run after the initial pass above.
Civil write test — ch08 moving loads / ch17 bridge (previously untested)¶
Built a minimal Civil model (30 m single-span concrete beam: 1 material, 1 section, 2 nodes, 1 element, 1 fixed support), then:
MovingLoadCode.update(/db/MVCD,CODE="AASHTO LRFD"): succeeded.TrafficLineLanes.create(/db/LLAN) first attempt failed with a real server-side semantic validation error, not a schema rejection:"[Error] Line Lane Data (Name:Lane1) contains errors.(Item:Centrifugal Force ( 0.0 < Value < 1.0))". The manual documentsLANE_ITEMS.CENT_Fas merely "optional (AASHTO LRFD only)" — this session's evidence is that onceAASHTO LRFDis the selected moving-load code,CENT_Fis effectively required (must be a value in the open interval (0, 1); the SDK's implicit default of omitting the field, which the server reads as 0.0, is rejected). Retried withCENT_F: 0.1— succeeded, andGETread back every field including server-filled defaults (GROUP_NAME: "",SKEW_START/END: 0,WHEEL_SPACE: 0,OPT_AUTO_LANE: False,ALLOW_WIDTH: 0,FACT: 0,SPAN_START: False,ECCEN_VERT_LOAD: 0) — full round-trip confirmed once the semantically valid payload was sent. Not an SDK bug (the field genuinely is optional per the manual's schema, and the SDK correctly makes it optional in theTypedDict), but worth knowing if you hit the same error live: pass a nonzeroCENT_FwhenMVCD.CODEis"AASHTO LRFD".BridgeGirderDiagram.create(/db/GSBG) with a placeholderBODY_ELEM_GRUP_K: 1(no structure group with that ID actually existed): succeeded — the server did not validate the group reference exists at write time.
Gen full analyze → results round-trip (post/* chapters, previously only mocked)¶
Added a static load case (STLD, NAME="DL", TYPE="D") and self-weight
(BODF, FV=[0,0,-1]) to the Gen cantilever-column model from the first
pass, then:
doc.analyze()(/doc/ANAL) — first call (no loads yet) correctly failed with"[Error] Load information has not been entered for Analysis."; after adding the load case, succeeded ("MIDAS GEN NX command complete").post.result_1.get_reaction_table/get_displacement_table/ get_beam_force_table— first call usedload_case_names=["DL"]and got back{"message": ""}for all three (looked like "no data", but was actually a caller mistake).get_table's own docstring inpost/base.pyalready documents the fix: load case names need a type suffix, e.g."DL(ST)". Retried with["DL(ST)"]— all three returned full documented{FORCE, DIST, HEAD, DATA}tables.- The numbers are physically correct, not just structurally valid:
- Reaction at node 1 (base):
FZ = 28.243152— matches hand-calc self-weight of a 0.6×0.6×3.2 m C24 concrete column (0.36 m² × 3.2 m × ~24.5 kN/m³ ≈ 28.2–28.8 kN). - Displacement at node 2 (free top):
DZ = -0.000005m — negligible axial shortening under self-weight, correct sign (downward). - Beam force: axial force
-28.24 kNat the I-end (base), decreasing linearly to0.00at the J-end (free top) across the 4 reported stations — exactly the expected self-weight axial-force diagram for a vertical cantilever.
This confirms the full chain end to end: SDK request shape → real Gen NX
solve → SDK response parsing, for both the /db/* write side and the
/post/TABLE read side.
Civil full analyze → results round-trip, including moving-load envelope results (previously untested)¶
A later Civil session closed the remaining gap on the Civil side: static
analysis physically verified end to end, and — Civil's signature feature —
an actual moving-load analysis run and its (MV:max)/(MV:min) envelope
results read back and sanity-checked.
Static self-weight round-trip — physically verified¶
Built a 30 m, 2-span (3-node) simply supported... in practice fixed
against rotation at both ends (constraint string 1111100 restrains
RY at both supports, not a true pin) concrete girder (0.6×1.0 m
rectangular section, C24/KS01(RC)), added a DL self-weight load case,
ran doc.analyze(), then read back reactions/displacements/beam forces:
- Total self-weight reaction:
ΣFZ = 423.647281 kNacross both supports (211.823641each) — consistent with a uniformly distributed self-weight loadw = 423.647 / 30 = 14.12 kN/mon a0.6 × 1.0 msection. - Support moment
MY = ±1059.11825 kN·m— matches the fixed-end-moment formula for a fixed-fixed beam under UDL,wL²/12 = 14.12 × 30² / 12 = 1059.12— an exact hand-calc match. - Beam-force moment diagram across both elements' I/1/4/2/4/3/4/J stations
forms a consistent parabola between the two support end-moments and the
midspan value (
132.39), matching continuous-beam bending-moment theory.
Operational note, not a bug: one Material.create attempt with
STANDARD: "KS(RC)" failed with "Failed to get material data for:
C24" — the correct Civil concrete standard code turned out to be
"KS01(RC)", not "KS(RC)". The manual doesn't enumerate every valid
STANDARD string per material type/product, so this is a live-only
finding, not a schema contradiction.
Moving-load analysis — full chain verified, with real vehicle/lane data¶
Added MovingLoadCode (CODE="KOREA"), a Traffic Line Lane spanning both
elements, a DB-24 Korean standard vehicle, and a moving-load case
referencing both, then re-ran analysis:
Vehicles.create(/db/MVHL) initially no-op'd silently ({"message": ""}, not an error, and a subsequentGETconfirmed nothing was actually saved) whenVEH_DEFAULTwas sent as{}. The manual's own worked example forSTANDARD_CODE="KS-RB"always populatesVEH_DEFAULTwith explicitDYN_LOAD_ALLOWANCE/CENT_Fvalues even though the schema marks those fields "optional" — copying the manual's exact worked example (MVLD_CODE: 6,VEH_DEFAULT: {"DYN_LOAD_ALLOWANCE": 0, "CENT_F": false}) succeeded immediately. Worth knowing live: don't send an emptyVEH_DEFAULT: {}even though every one of its fields is individually documented as optional — populate it per the manual's worked example for yourSTANDARD_CODE.MovingLoadCase.create(/db/MVLD,TYPE=0general load referencing the vehicle + lane by name) — succeeded.doc.analyze()— succeeded in 2.0s (tiny model, no large-model delay here).get_beam_force_table(load_case_names=["MV1(MV:max)"])and["MV1(MV:min)"]— both returned full, real, non-degenerate envelope data: e.g. max positive midspan moment615.61–732.12 kN·m, min (most-negative) support moment-1702.53 kN·m(larger in magnitude than the plain self-weight case above, as expected — the DB-24 truck adds to the fixed-end moment at whichever support it's nearest). This is a believable, non-trivial moving-load envelope, not placeholder/zero data.
This confirms the full Civil-specific chain end to end: MVCD → MVHL →
LLAN → MVLD → doc.analyze() → post/TABLE with (MV:max)/(MV:min)
suffixes, previously only exercised as isolated writes (see the moving-load
write test above), not run through an actual analysis.
Operational quirk shared with the Gen findings: a confirmation dialog blocks the whole API session, not just one call¶
Partway through this session, MovingLoadCode.update (changing the active
moving-load code) triggered a Civil NX confirmation dialog ("changing
this will delete existing analysis results") that the user hadn't
dismissed yet. While that dialog sat open, every subsequent API call —
including totally unrelated ones like a plain GET /db/NODE — timed out
with no response, not just the call that triggered the dialog. After the
user dismissed the dialog, the session immediately became responsive
again, and a GET on the field that triggered it confirmed the change had
already persisted despite the client-side timeout.
This is the same shape of finding as the Gen CC-ANAL stall (an API call
blocks on an unacknowledged UI dialog, and the underlying change completes
and persists regardless of whether the HTTP response ever arrives) — but
milder and expected: a normal user-confirmation dialog is not a bug, and
it explains itself once you know to check for it. Worth knowing for
scripted/batch use of this API: any call that can trigger a confirmation
dialog (destructive/data-loss-risking changes) can make the entire session
appear hung until a human dismisses it, not just that one call.
⚠️ CONFIRMED — CC-ANAL (RC column code-check perform) reproducibly stalls Gen NX at "Converting Design Results 0%" (often requires a forced process kill)¶
STATUS UPDATE (2026-07-25):
CC-ANAL/BC-ANAL/WC-ANALran clean 4/4 — on the same build that hung here. This is not a "fixed in a newer build" story: both the reproductions below and the clean re-runs happened on Gen NX 2026 (v2.1), build 06/23/2026. The trigger is therefore something other than the build, and is still unidentified — so nothing in this section is retracted. See the re-verification section below.
While extending the same Gen session above to verify design-code check execution (as opposed to just config-singleton writes), the following sequence crashed/hung the Gen NX desktop application itself (not just an API error — the process stopped responding and required a manual restart):
design.rc_kds.setup.ModifyMemberType.create({1: {"TYPE": "COLUMN"}})— succeeded.design.rc_kds.rebar.ModifyColumnRebarData.create({1: {"ITEMS": [...]}})keyed by section number 1 (main bar D22×8, end/center hoop bars D10) — succeeded.- First
perform_column_check({"PERFORM_TYPE": "ALL"})(CC-ANAL) — failed cleanly with{"error": {"message": " Please perform analysis."}}(expected — design parameters changed after the last solve, invalidating results). - Re-ran
doc.analyze()— succeeded. - Retried
CC-ANAL— failed cleanly with{"error": {"message": "failed:LoadCombination"}}(also expected — no load combination existed yet, only a raw load case). - Added
db.load_combinations.LoadCombinationGeneral.create({1: {"NAME": "COMB1", "ACTIVE": "ACTIVE", "iTYPE": 0, "vCOMB": [{"ANAL": "ST", "LCNAME": "DL", "FACTOR": 1.2}]}})— succeeded. - Retried
CC-ANALa third time — this call never returned. Timed out client-side at both 30s (default) and 120s (explicitMidasClient(timeout=120)) withReadTimeoutError/ConnectionError— no HTTP response at all, not even a slow one. The user confirmed the Gen NX desktop application itself had stopped responding and needed a full restart.
Reproduction #2 — confirmed, with visual evidence¶
To rule out "messy session state" as the cause, the entire sequence was
redone from a fresh doc.new_project(), touching only RC design (no
steel_kds/src_aiksrc2k namespaces at all this time):
- Fresh minimal model (1 material, 1 section, 2 nodes, 1 element, 1
support, 1 static load case
DL, self-weight) +doc.analyze()— all clean. ModifyMemberType→COLUMN,ModifyColumnRebarData(same rebar payload as before),doc.analyze()again — all clean.CC-ANALbefore any load combination existed — failed cleanly and fast with{"error": {"message": "failed:LoadCombination"}}. Did not hang. This suggested the first hang might have been session-state related.- Manually written
db.load_combinations.LoadCombinationGeneralcombo —CC-ANALretried — failed cleanly and fast again with the same"failed:LoadCombination"message (the manually-enteredLCOM-GENentry apparently isn't recognized as a valid design combination by the RC check module — a separate, milder finding, see below). - Used
ope.generate_load_combination_concrete({"OPTION": "ADD", "DGNCODE": "KDS 41 20 : 2022"})instead — this is the right way to produce design-code-recognized combinations: it auto-generatedcLCB1(1.4(D),ACTIVE: "STRENGTH") andcLCB2(SERV:(D),ACTIVE: "SERVICE") in/db/LCOM-CONC. CC-ANALretried a third time, now with a real, code-recognized design combination in place — hung again, this time with a 40s explicit client timeout.- The user checked the Gen NX window directly and found a "Stop Design Thread" dialog, stuck at "Converting Design Results... 0%" with a "Stop Execution" button. Clicking Stop Execution did nothing — the dialog stayed frozen. The application had to be force-killed via Task Manager; there was no graceful recovery path.
Conclusion: this is a real, reproducible Gen NX application defect,
not session-state flakiness and not an SDK issue. CC-ANAL (RC column
code-check "perform") appears to spawn an internal "Design Thread" that
can deadlock during its "Converting Design Results" phase, and the
deadlock is unrecoverable from the UI (Stop Execution is on the same
stuck thread/message loop). The Open API call blocks synchronously
waiting for that thread, so from the SDK's perspective it just looks like
a network read timeout with no way to distinguish "still computing" from
"permanently stuck" short of an arbitrarily long timeout.
Reproduction #3 — confirmed a third time, on an unrelated large real-world model¶
To rule out "this only happens on a tiny synthetic 1-element model," the same pattern was tested against a separate, pre-existing production-scale Gen model (thousands of nodes/elements, real materials/loads/analysis results already present, different RC design code originally selected). Project-specific numeric details are intentionally omitted here — only the reproduction pattern matters:
- A read-only survey confirmed this model's active RC design code was
not KDS 41 20:2022, so its
rc_kds.setup(config-singleton) andrc_kds.rebar(member rebar) tables were empty for every member, despite the model already having real design-member registrations under a different code. CC-ANALon a real, verified-vertical concrete column element withPERFORM_TYPE: "ELEMS"(single element, not"ALL") — failed cleanly and fast:{"error": {"message": "failed:Rebar, BeamData"}}. No hang.- Assigned
ModifyMemberType(COLUMN) +ModifyColumnRebarData(same rebar shape as the earlier reproductions) for that element's section, re-randoc.analyze()(succeeded, no issue at this model's larger scale) — retriedCC-ANAL— failed cleanly and fast again:{"error": {"message": "failed:LoadCombination"}}(the model's 50+ pre-existing general load combinations were not recognized as valid design combinations, consistent with reproduction #2). No hang. - Ran
ope.generate_load_combination_concrete({"OPTION": "ADD", "DGNCODE": "KDS 41 20 : 2022"})— succeeded, auto-generated proper factored combinations from the model's real load cases. - Retried
CC-ANALa third time on the same single element — hung again, 40s client timeout, no HTTP response. The user confirmed Gen NX was frozen again and required another forced kill.
This is now the exact, minimal, deterministic trigger pattern across
three independent reproductions (one synthetic model, one real
production model tested twice): CC-ANAL hangs specifically once both
(a) the target element/section has member-type + rebar data assigned for
the KDS 41 20:2022 namespace, and (b) at least one load combination
exists that the KDS check module itself recognizes as a valid design
combination (i.e., one generated via ope.generate_load_combination_concrete
or equivalent, not a plain manually-written /db/LCOM-GEN entry). In
other words: it hangs at the exact moment the check has enough real data
to actually attempt the P-M-interaction calculation — every precondition
short of that fails fast and cleanly instead.
Do not call design.rc_kds.checks.perform_column_check (CC-ANAL)
against a live session with both preconditions satisfied, without an
escape plan (expect to force-kill Gen NX — "Stop Execution" does not
work). Given the shared "Design Thread" architecture across every other
perform_*_check/*-ANAL function in this SDK (perform_beam_check,
perform_brace_check, perform_wall_check in the same file;
perform_steel_code_check in steel_kds.py;
perform_src_beam_check/perform_src_column_check in
src_aiksrc2k.py; perform_optimal_design/OCHECK variants), treat
the entire "perform design check" family as carrying the same likely hang
risk once both analogous preconditions are met until each is
independently tested. This session did not attempt any of the others.
Reproduction #4 — cleanest case: a natively KDS-configured real model, no forced setup¶
A fourth session opened a separate, unrelated pre-existing Gen model whose
RC design code was already "KDS 41 20 : 2022" natively (not forced
onto it like reproductions #2/#3) — rc_kds.setup.ConcreteDesignCodeOption
and rc_kds.rebar.ModifyColumnRebarData already had real data (4 sections)
before any of this session's calls. This is the most realistic scenario
yet: a normal user, on a normal KDS-native model, checking one column.
CC-ANALon a real column element (PERFORM_TYPE: "ELEMS", single element) — failed cleanly:{"error": {"message": " Please perform analysis."}}.doc.analyze()via the API returned"MIDAS GEN NX command complete", but a subsequentget_reaction_table()call still reported"[empty] Cannot generate table data as there is no analysis result."— i.e. the API's "complete" acknowledgment did not reliably mean results were actually queryable yet on this larger model (4044 nodes). Re-running the analysis from the Gen NX GUI directly resolved this (reaction table then returned real data) — worth noting as a separate, milder finding: don't assumedoc.analyze()'s response means results are immediately queryable for large models; confirm with a cheap results call before proceeding, or retry.CC-ANALretried on the same element — the HTTP call hung again (40s timeout, no response), and the same "Stop Design Thread — Converting Design Results... 0%" dialog appeared. This time the user also checked Gen NX's internal message/log window while it was stuck, and it showed the entire check had already finished:
*** Start Code Checking by KDS 41 20 : 2022.
End preparing Design Informations.
End Design/Checking of Member.
Creating design result file...
End creating design result file.
End converting Design Results.
*** End Code Checking by KDS 41 20 : 2022
Every step, including "End converting Design Results" and the final "End Code Checking" line, had already logged as complete — while the progress dialog was still frozen at 0% on that same step. The user clicked Stop Execution; the dialog closed and the app recovered cleanly, no forced kill needed this time (unlike reproductions #1–#3, where an additional error popup appeared and the app had to be force-killed).
Revised diagnosis: this is very likely a stuck progress-dialog / stale completion-signal bug, not a genuine backend computation deadlock. The underlying KDS code check appears to actually finish — the message log says so explicitly, end to end — but something (the progress dialog's own close/refresh logic, and/or whatever signal the Open API layer itself waits on to consider the request "done" and return an HTTP response) never fires. That would also explain why the API call kept timing out even in this 4th case: it's plausibly blocked on the same stuck signal as the dialog, not on the design check itself. Reproductions #1–#3 didn't have this log checked at the time, so it's unconfirmed whether they were the same underlying issue or a genuinely different (deadlocked, not just signal-stuck) failure — worth checking the message log first thing if this is reproduced again.
Four for four on the stall itself; consequences vary. Every attempt
where the check had a real target element with real rebar data, a real
recognized load combination, and real analysis results triggered the same
"Converting Design Results... 0%" stall — including this cleanest case
with no artificial setup at all and, per the log, a design check that had
actually completed. That part is no longer a corner case; it's the
expected outcome of calling CC-ANAL for its actual intended purpose in
this Gen NX build. What differs is the outcome: an additional error popup
and a required forced kill 3 of 4 times, vs. a clean recovery via Stop
Execution (with the check apparently having already finished
successfully) the 4th time. Testing was stopped after this
reproduction — no further value in repeating it, and most attempts
still cost a forced restart or at least a stuck dialog.
Confirmed: the check result is actually there — CC-TABLE proves it¶
After reconnecting post-reproduction #4, get_column_check_table (CC-TABLE)
was called for the same element (371) that the "hung" CC-ANAL call had
targeted — it returned full, real design-check results:
CHK_STR: "OK", CHK_RBR: "OK", real P-M interaction ratios
(Rat_P: 0.468, Rat_M: 0.151), real assigned rebar ("28-6-D25"), real
shear/hoop-spacing checks — not placeholder or empty data. This
conclusively confirms the diagnosis above: the design check itself
genuinely completes and its results genuinely persist to the model, even
when the triggering CC-ANAL call times out with no HTTP response.
Practical workaround for callers: if perform_column_check (CC-ANAL)
times out or the connection errors, don't assume the check failed —
retry with get_column_check_table (CC-TABLE) for the same
element/section shortly after. The check very likely already ran to
completion; only the "done" acknowledgment got lost, not the work.
Reproduction #5 — retried with Gen NX run as Administrator: same result¶
To rule out a permissions/UAC-related cause, the user closed Gen NX, relaunched it with "Run as administrator," reopened the same natively-KDS-configured model from reproduction #4, and re-ran the full analysis from the GUI before retrying.
perform_column_check(CC-ANAL) on the same element (371) — the API call hung again, timing out client-side after 60s with no HTTP response (MidasConnectionError: ... Read timed out), and the Gen NX progress dialog stalled at "Converting Design Results... 0%" again, same as every prior reproduction.- The user waited briefly without clicking Stop, then clicked Stop Execution — the dialog closed and the app recovered cleanly, no forced process kill needed (matching reproduction #4's recovery behavior, not the forced-kill behavior of #1–#3).
get_column_check_table(CC-TABLE) for the same element immediately after — returned the identical full result set as reproduction #4 (CHK_STR: "OK",CHK_RBR: "OK",Rat_P: 0.468,28-6-D25, ...), confirming the workaround holds here too.
Conclusion: administrator privileges are not a factor. The stall, the Stop-Execution recovery path, and the CC-TABLE workaround are all identical running elevated vs. running normally — this rules out a UAC/file-permission explanation for the stuck dialog.
Build/localization confirmed: this is the English/international build,
not a Korean-localized one. All five reproductions were run against
Gen NX 2026 v2.1, English version — so the stall is not a
Korean-UI-localization artifact; it reproduces on the same build
international users install. What remains genuinely untested is the
design-code axis: every reproduction used KDS 41 20 : 2022
specifically. It's still an open question — not tested here — whether the
same "Converting Design Results 0%" stall occurs for non-Korean design
codes (AISC, Eurocode, etc., via steel_kds.py or other design/*
modules) or is somehow specific to the KDS check module's own
implementation. Don't generalize "every perform_*_check call hangs" past
KDS 41 20:2022 without independent testing of another code.
perform_wall_check (WC-ANAL) — tested, does NOT reproduce the stall¶
Using a sixth session (a separate, wall-heavy Korean production model, KDS
41 20:2022 native, with pre-existing real wall-check results already present
from prior GUI use — WID/Story/WallMark rows like 101/B1/RW1
with real CHK_STR: "OK" data), perform_wall_check was tested for the
first time:
- Single wall/story (
SELECTIONS: [{"WALL_IDS": {"KEYS": [101]}, "STORY": ["B1"]}]) — returned{"message": "success"}in 3.5s. No stall. - All walls/stories (
SELECTIONSomitted) — returned{"message": "success"}in 5.9s. No stall.
WC-ANAL does not reproduce the CC-ANAL stall, at least on this
model, in either single-target or full-scope form. This is useful negative
evidence: whatever's stuck in the column-check "Design Thread" progress
dialog is not a blanket property of every perform_*_check function in
this file — it may be specific to CC-ANAL (or to the member-based
ELEMS/SECTIONS-targeted check family: beam/column/brace, vs. the
WID+STORY-targeted wall check, which may run through different internal
code entirely). Don't assume perform_wall_check carries the same risk as
perform_column_check going forward.
perform_beam_check (BC-ANAL) — CONFIRMED to hang too, on two separate models, with a new crash variant¶
Same wall-heavy-model session, real beam rebar data already present
(ModifyBeamRebarData had entries for elements 11, 12, 13). Walked
the same precondition sequence as the CC-ANAL reproductions:
BC-ANALon element 11 before member type was registered in this design-code namespace — failed cleanly and fast:{"error": {"message": "failed:Rebar"}}(ModifyMemberType.get()was empty for this namespace even though rebar data existed — same "each precondition fails independently and cleanly" pattern as the column reproductions).ModifyMemberType.update({11: {"TYPE": "BEAM"}})— succeeded.BC-ANALretried — failed cleanly:{"error": {"message": " Please perform analysis."}}(member-type change invalidated prior results, same as CC-ANAL reproduction #1/#4).doc.analyze()via the API — timed out after 90s with no response. This one was just a genuinely long-running solve on this large model (4000+ nodes), not the progress-dialog bug — the user confirmed Gen NX's own progress UI was actively solving, not stuck. See the timeout-guidance note below. After the user re-ran analysis from the GUI and confirmed it completed,BC-ANALwas retried:BC-ANALretried a third time on element 11 — hung again, 60s client timeout, no HTTP response. This time the user reported the Gen NX app itself looked normal (no visible stuck dialog) — butget_beam_check_table(BC-TABLE) for that same element also then hung repeatedly (multiple retries, up to 30s each), whileBC-TABLEfor a different, nonexistent element (12 — actually absent from this model, confirmed by the clean fast"Element 12 does not exist."response) returned instantly. Basic connectivity (GET /db/NODE) also remained fully responsive throughout. This points to something specifically locked/stuck server-side scoped to that element's beam check state, even without a visibly stuck dialog — consistent with the "stuck signal, not a real deadlock" diagnosis, just manifesting without an obvious UI symptom this time.
A second, independent model was then opened to rule out anything
specific to the wall-heavy model: a real production Taiwan RC frame
("rahmen") structure, 315 nodes / 564 elements, active design code
TWN-USD112 (not KDS — so, like reproduction #3, the KDS module's own
MBTP/REBB tables were empty and had to be populated directly, which
the API allowed with no validation against the model's "actual" active
code, consistent with prior findings):
ModifyMemberType.update({1: {"TYPE": "BEAM"}})(element 1, a realTYPE: "BEAM"element, section 11) — succeeded.ModifyBeamRebarData.update({11: {"ITEMS": [...]}})(keyed by section number 11, matching element 1'sSECT) — succeeded.doc.analyze()— this model is much smaller; completed in 16.4s, and a subsequentget_reaction_table(load_case_names=["DL(ST)"])call confirmed real, queryable results immediately (no large-model delay this time).ope.generate_load_combination_concrete({"OPTION": "ADD", "DGNCODE": "KDS 41 20 : 2022"})— succeeded, generated a full set of KDS strength combinations (cLCB1..cLCB7+) from this model's real load cases (DL,LL,EXN,EXP,EYN,EYP,EZ,WX,WY).BC-ANALon element 1, all preconditions now satisfied on this fresh model — hung again, 60s client timeout, no HTTP response.- This time the user reported the exact text of the crash dialog for the
first time — "[Error] Failed to disconnect the work session due to an
unidentified error. Since you have not logged out, other PCs may have
limited access to the license. In order to properly terminate the
program, try to re-execute the program, press 'New Project' and then
close the program." — and the application crashed/closed. The user
confirmed this is the same popup that appeared during the earlier
forced-kill
CC-ANALreproductions (#1-#3) — it just hadn't been transcribed verbatim before (previously logged generically as "an additional error popup"). The user's stated rule: whenever this specific license-work-session-disconnect popup appears, the program always dies — there is no recovering from it. This is a different, worse outcome than the clean "Stop Execution" recovery seen in reproductions #4/#5 (where this popup did not appear at all).
This confirms BC-ANAL shares CC-ANAL's hang, on two independent
models (one forced-KDS-setup wall-heavy Korean model, one forced-KDS-setup
Taiwan RC frame model), both under the same precondition pattern
(member-type + rebar assigned in the KDS namespace, a KDS-recognized load
combination present, confirmed-queryable analysis results). Combined
with the earlier CC-ANAL reproductions, the observed outcomes now form
two consistent buckets rather than random variation: (a) the "Converting
Design Results" dialog gets stuck but recovers cleanly via Stop Execution,
no popup, no crash (CC-ANAL reproductions #4/#5) — the check likely did
finish and its results persist (confirmed via CC-TABLE); or (b) the
"Failed to disconnect the work session" license-error dialog appears
and the program crashes outright, unrecoverable (CC-ANAL reproductions 1
through 3, and this session's Taiwan-model BC-ANAL reproduction). This
raises the severity of the underlying MIDASIT bug report beyond "a stuck
progress bar" — outcome (b) is a licensing-visible crash that, per the
dialog's own text, may affect other PCs' access to the license until the
process is fully terminated.
This exact crash signature has a prior precedent — it isn't new to v2.1¶
A separate, earlier round of live MIDAS Gen NX Open API testing (same
account, a different local project, pre-v2.1 build) independently hit the
identical "Failed to disconnect the work session..." crash text —
from a completely different trigger: calling doc/new rapidly and
repeatedly (back-to-back x10, concurrent x5/x15). That round's writeup
retested the doc/new trigger specifically against the v2.1 build and
found it no longer reproducible, and closed it out as resolved,
concluding the original crash was "limited to a previous build or a
specific sequence." This session's CC-ANAL/BC-ANAL reproductions
show that conclusion was premature — the same crash signature, verbatim,
resurfaces under a different trigger (a design-check "perform" call) in
the same v2.1 build. This is valuable context for framing a MIDASIT bug
report: not a one-off, but a recurring session-teardown defect that keeps
resurfacing under different trigger conditions across builds and across
independent testing sessions — worth reporting as a class of bug rather
than a single reproducible steps list tied to one endpoint.
Useful diagnostic tool for next time: that same earlier round
identified GET https://moa-engineers.midasit.com:443/mapikey/verify
(note: base URL with the product path — /gen or /civil — removed) as a
live-verified health-check endpoint, undocumented in the vendored manual,
that reliably distinguishes "AWS relay server alive, Gen NX process alive"
(status: "connected") from "AWS relay alive, Gen NX process died"
(other endpoints then return HTTP 404 with "Client Disconnected" /
"client does not exist", while /mapikey/verify itself still resolves
against the relay). This wasn't used during today's reproductions — relied
on the user's visual confirmation of the Gen NX window instead — but would
give a definitive, scriptable way to confirm process death vs. a merely
slow/busy session in future reproductions, without waiting on manual
screen-watching.
perform_wall_design (WD-ANAL) — CONFIRMED to hang too, even though the sibling perform_wall_check (WC-ANAL) does not¶
Same wall-heavy model, same wall (WID 101, Story B1) already confirmed
clean under WC-ANAL. perform_wall_design (a different endpoint,
manual item #48, WD-ANAL — RC Wall Design Perform, distinct from
WC-ANAL's RC Wall Check Perform, item #63) was tested for the first
time:
- First attempt — failed cleanly and fast:
{"error": {"message": " Please perform analysis."}}. Unrelatedsteel_kds/src_aiksrc2kmember-type writes made earlier in this session (while probing whether other design-code modules share theCC-ANALbug — see above) apparently invalidated the model's analysis results, consistent with the established "any design-parameter write invalidates results" pattern. - Re-ran
doc.analyze()— completed in 151.3s (large model, expected per the timeout-guidance note below, not a stall). WD-ANALretried on the same wall/story — hung, 60s client timeout, no HTTP response. The user checked Gen NX and reported the app looked completely normal — no visible stuck dialog this time either.get_wall_design_table(WD-TABLE) for the same wall/story — returned full, real design results immediately (Pu: 126.106,Rat-Py: 0.748,phiVn: 1804.81,Rat-V: 0.634, real rebar"D13 @300"/"D10 @190",CHK: "OK"). Same workaround asCC-ANAL: the design computation completed and persisted; only the HTTP acknowledgment never arrived.
Separate schema finding, unrelated to the hang: the manual documents
WD-TABLE's response as {"data": {"COMPONENTS": [...], "ROWS": [{col:
val, ...}, ...]}} (with a worked example iterating data["ROWS"]), but
the live response above came back in the same {"Result Table": {"FORCE":
..., "DIST": ..., "HEAD": [...], "DATA": [[...], ...]}} HEAD/DATA shape
every other member-check table in this chapter uses. Confirmed by
re-reading the manual's own JSON Schema and worked example for this
endpoint side by side with the live response — this isn't a caller
mistake. get_wall_design_table's docstring now flags this.
This means the earlier "WID+STORY-targeted checks are safe" hypothesis
(based on WC-ANAL alone) was wrong — WD-ANAL is also WID+STORY-
targeted and still hangs. The safe/unsafe split isn't about the targeting
scheme (ELEMS/SECTIONS vs WID/STORY); perform_wall_check
(WC-ANAL) remains the only tested "perform" function in this file so far
that does not reproduce the stall — perform_column_check,
perform_beam_check, and now perform_wall_design all do, across every
model tested. What distinguishes WC-ANAL from the other three is still
unclear — possibly that a wall "check" only reads/verifies existing rebar
against demand, while WD-ANAL/CC-ANAL/BC-ANAL all compute and
write new required-design data back into the model (rebar layout,
member-check parameter records) — but this is a hypothesis, not confirmed.
Other design-code modules (steel_kds, SRC) — inconclusive, blocked by license/model limitations, not evidence either way¶
While looking for a fast way to test whether the CC-ANAL-style stall
extends beyond the RC-KDS module, two other design-code "perform check"
families were attempted on the same wall-heavy model and both were blocked
before reaching the actual "perform" call, for reasons unrelated to this
bug:
steel_kds.perform_steel_code_check: blocked at the load-combination step —ope.generate_load_combination_steelreturned{"error": {"message": "There is no license to use the specified Steel Design Code."}}. A real license limitation, not a bug; this account/model cannot exercise the steel design-check module at all.- SRC (
src_aiksrc2k): the design code itself is licensed (ope.generate_load_combination_srcsucceeded), butSrcBeamSectionData(the rebar-equivalent registration for SRC composite beams) returned{"error": {"message": "Unknown Error"}}on every attempt, andSrcModifyMaterialwrites didn't appear to persist (GETcame back empty afterward). Likely cause: every section in this model is a plain rectangular RC shape (SB) — SRC (steel-reinforced-concrete composite) design plausibly requires an actual composite section geometry that doesn't exist anywhere in this model, unlike the RC-KDS rebar overlay which worked on any section regardless of the model's "real" design code.
Neither result says anything about whether steel/SRC "perform check"
calls share the CC-ANAL stall — both were blocked upstream of the
precondition chain this file's other reproductions establish as necessary
(member type + rebar/section data + recognized load combination +
queryable analysis results). Testing this properly would need a model with
real steel or SRC composite members, not a forced setup on an RC-only
model. Left as a genuinely open question.
Timeout guidance for doc.analyze() on large models — a separate, milder finding¶
While waiting on step 4 above, it became clear a plain client-side read
timeout on doc.analyze() is not by itself evidence of a hang — on a
4000+ node model, analysis can legitimately take longer than
MidasClient's default 30s timeout (observed: still running past 90s,
with the user confirming Gen NX's own progress UI showed it actively
solving, not stuck). Don't conflate this with the CC-ANAL stuck-dialog
bug — that one is confirmed via the message log to have a specific
"finished but dialog didn't update" signature; a slow doc.analyze() on a
big model is just... slow. Pass a larger MidasClient(timeout=...) for
big-model analysis calls rather than treating a timeout as failure.
Practical takeaway for this SDK: nothing to fix in midas-nx itself —
every request shape sent was correct per the manual (confirmed by the
clean, correctly-shaped {"error": ...} responses on every call that
didn't hang, across four separate reproductions on three different
models), and the SDK has no way to add a client-side guard against a
server-side dialog/signal bug it can't see. This is a confirmed,
reproducible Gen NX application defect worth reporting to MIDASIT
directly — most usefully framed as "the KDS column-check progress dialog
gets stuck at 'Converting Design Results 0%' even after the message log
shows the check completed, and the Open API call that triggered it never
receives a response either," together with this file's exact precondition
pattern (member-type + rebar assigned, plus a KDS-recognized design load
combination present, plus confirmed-queryable analysis results) —
reproduced 4 for 4 times that all conditions were met, across a trivial
synthetic model, a production-scale model (twice), and a natively
KDS-configured production model.
Repeatable smoke test — scripts/live_smoke.py (2026-07-22)¶
Everything above was run by hand, one call at a time. This session turned
the core write → analyze → read round trip (new project, unit, material,
section, node, element, support, load case, self-weight, doc.analyze(),
reaction/displacement/beam-force tables, checked against a hand-calc) into
scripts/live_smoke.py, then ran it fresh against both a live Gen NX and a
live Civil NX session (same MIDASIT account, both freshly reset via
/doc/NEW). Both runs succeeded end to end and reported
reaction_matches_hand_calc: true. Confirmed builds (via each app's
About/도움말 dialog): MIDAS Gen NX 2026 (v2.1), build 06/23/2026 and
MIDAS Civil NX 2026 (v2.1), build 06/05/2026 — the same v2.1 line as
the English-build Gen NX used in the CC-ANAL/BC-ANAL reproductions
above, now with an exact build date on record for the first time.
docs/coverage.json now tags the ten endpoints this script exercises
(/doc/NEW, /doc/ANAL, /db/UNIT, /db/MATL, /db/SECT, /db/NODE,
/db/ELEM, /db/CONS, /db/STLD, /db/BODF) with a "live_verified"
field carrying this date and both builds; ROADMAP.md surfaces the count
and a Gen/Civil build matrix generated from it (PLAN.md's D4).
New findings from this run, beyond what's already recorded above:
- The
/post/TABLEresponse's top-level key is not stable across sessions. Earlier findings in this file saw it as"Result Table"; this session got back the literal string"empty"for the same reaction/displacement/beam-force calls (withtable_nameleft at its default""). Don't hardcode either key —scripts/live_smoke.py's_find_head_data()scans the response's values for the first dict containing both"HEAD"and"DATA"instead, which is robust across whatever this key turns out to be call-to-call. - A 200 response can carry an
{"error": ...}body even from aDbResource.create()call, and this is easy to miss if a caller only checks the HTTP status.live_smoke.py's first draft didn't check for this and silently treated a failedConstraint.create()as successful — the fix (checking for an"error"key in every 2xx body) is the same pattern already noted above for design-code "already exists" responses; worth treating as a general rule for everyDbResource/doc.*call, not just the design-code family. - Gen's concrete
STANDARDcode for C24 is"KS01(RC)", same as Civil — confirmed by first trying the plausible-looking"KS(RC)"on Gen, which failed with{"error": {"message": "Unknown Error"}}(an unhelpful message with no hint about the actual problem being the standard-code string), then retrying with"KS01(RC)", which succeeded immediately. The manual doesn't enumerate validSTANDARDstrings per product, so — as with the original Civil finding above — this is a live-only data point, not a schema contradiction. - Physically-grounded cross-check, both products: a 0.6×0.6 m, 3.2 m
tall C24/KS01(RC) cantilever column's self-weight reaction came back as
FZ = 27.113426 kNon both Gen and Civil (byte-identical, same model) — about 4% below a naive hand-calc using a generic 24.5 kN/m³ unit weight (0.36 m² × 3.2 m × 24.5 ≈ 28.22 kN), implying MIDAS's actualC24/KS01(RC)preset unit weight is closer to27.113 / (0.36 × 3.2) ≈ 23.53 kN/m³. Useful reference point for anyone else hand-calc-checking a KS01(RC) C24 model;scripts/live_smoke.pyuses a 5% tolerance for exactly this reason rather than an exact-match assertion.
STATUS UPDATE (2026-07-25) — CC-ANAL/BC-ANAL ran clean on the same build that hung¶
The *-ANAL design-check stall documented at length above — the single
most severe finding in this file, reproduced across three models and five
attempts, and the reason every perform_* docstring in design/ carried a
"treat as a hang risk" warning — did not reproduce in a later round of
live use. Critically, this is not a "fixed in a newer build" story.
Source of this finding. Unlike the rest of this file, this did not come
from a dedicated SDK test session. It comes from QuickRebar NX
(rebar-repair.vercel.app), a separate production web tool by the same
author that drives the same Gen NX Open API endpoints over HTTP. Its own
project notes record a live re-test on 2026-07-25 that ran:
CC-ANAL— including the whole-modelPERFORM_TYPE: "ALL"variant, the exact shape that reproducibly killed the app in the reproductions aboveBC-ANAL— the beam equivalent, also previously confirmed to hangWC-ANAL— the wall check, which never reproduced the stall anyway
4 out of 4 clean, with the app staying connected throughout.
PERFORM_TYPE: "ALL" re-checks an entire model in roughly 2 seconds. The
tool now ships BC-ANAL wired to a user-facing "Gen NX 재검토" button,
followed by a BC-TABLE read of CHK_STR / Rat-N / Rat-P / Rat-V —
i.e. this is not a one-off lab result but an endpoint running in a
deployed tool against real users' models.
⚠️ The build did not change — so the trigger is still unidentified¶
The About dialog was checked on 2026-07-25 and reports MIDAS Gen NX 2026
(v2.1), build 06/23/2026 — byte-for-byte the same build recorded for the
CC-ANAL/BC-ANAL reproductions above and for the 2026-07-22 smoke test.
The same binary both hangs and does not hang.
That rules out the most comforting explanation (a vendor patch) and leaves the actual variable unknown. Plausible candidates, none of them tested:
- Model state. The reproductions established a precondition chain
(member type + rebar + a KDS-recognized load combination, the last one
generated via
ope.generate_load_combination_concreterather than a hand-written/db/LCOM-GEN) and hung at the exact point the check had enough real data to attempt the P-M calculation. Whether the QuickRebar models satisfied that same chain was never checked. - Call sequence. The reproductions ran the check immediately after API-side writes of member-type/rebar data; the tool's flow may differ in ordering or in how much settles first.
- Client/transport. Python
requestsfrom a local machine vs. Nodefetchfrom a Vercel serverless function — different timeouts and connection handling against the same relay. - Genuine intermittency in the trigger. The defect was already known to be intermittent in its consequences (forced kill 3 of 5 times, clean "Stop Execution" recovery the other 2). Intermittency in the trigger itself was assumed absent, but four clean runs do not disprove it.
Nothing in the reproduction sections above is retracted. The correct reading is "this is not universal and not always triggered," not "this is fixed."
What this does and does not license¶
| Endpoint | Earlier status (build 06/23/2026) | 2026-07-25 (same build) |
|---|---|---|
CC-ANAL (column check) |
hung 5/5 | ✅ clean 4/4 |
BC-ANAL (beam check) |
hung on 2 models | ✅ clean |
WC-ANAL (wall check) |
never hung | ✅ still clean |
WD-ANAL (wall design) |
hung | ❓ not re-tested |
BRC-ANAL, steel CODE-ANAL, SRC *-ANAL, OCHECK |
never tested | ❓ not re-tested |
One further limit on the evidence: KDS 41 20:2022 only. Every test on
both sides of this — the original reproductions and the re-verification —
used the KDS RC design code. Nothing here says anything about the steel
(steel_kds.py), SRC (src_aiksrc2k.py), or non-Korean code paths.
Keep the defensive pattern — this is now the main takeaway¶
Because the trigger is unidentified rather than removed, the defensive pattern is not optional hygiene; it is the actual mitigation. The production tool wraps these calls this way, and the SDK's docstrings recommend the same:
- Short timeout on the
*-ANALcall (the tool uses 25s). - Read the
*-TABLEback regardless of whether*-ANALreturned. This was the confirmed workaround during the hang era — a timed-out check had usually completed and persisted anyway — and it remains the correct shape for a call whose HTTP acknowledgment is known to be unreliable. - Prefer reading
*-TABLEalone when possible. Design results the user already computed in Gen NX's own GUI are readable with no perform call at all. That path never carried any of this risk and is still the zero-risk option.
Why the docstrings kept the history¶
The perform_* docstrings in design/ now lead with this re-verification
rather than the old "never call this" rule, but they retain a compressed
version of the reproductions. That is deliberate, and the same-build
finding above is why: this exact crash signature ("Failed to disconnect
the work session...") has now resurfaced twice under different triggers
— once via rapid doc/new calls on a pre-v2.1 build, once via *-ANAL on
v2.1 — and was declared resolved after the first. Treating "did not
reproduce" as "fixed" is the mistake this file has already recorded once,
and the first draft of this very section repeated it by assuming a newer
build was responsible before the About dialog was actually checked.
2026-07-26 — read-only sweep reproduced on a real model, same build¶
Re-ran the whole GET surface with the new scripts/live_readonly_sweep.py
(read-only; safe against an open document, unlike live_smoke.py) against a
real production model — 710 nodes, 1272 elements, 60 sections, with
analysis results already present — on the same build as the 2026-07-22
session (Gen NX 2026 v2.1, build 06/23/2026).
| 2026-07-22 (blank model) | 2026-07-26 (710-node model) | |
|---|---|---|
| GET-capable Gen resources swept | 253 | 253 |
| Answered | 233 | 233 |
| Failed (all 404) | 20 | 20 |
Civil NX 2026 (v2.1), build 06/05/2026 was swept the same day and also reproduced exactly — 293 swept, 273 answered, 20 404s. The Civil session had a near-empty document (2 nodes, 1 element), so unlike the Gen run it is a same-conditions repeat, not a different-model one.
The failing 20 are the same 20 endpoints, all MidasNotFoundError 404:
the 13 Hyper-S -M1 routes plus CMCS, EWSF, PLCB, RCHK, SPAN,
STRPSSM, WVLD. Run twice in the same session, byte-identical both times.
What this does and does not settle:
- Model state is eliminated as an explanation. The two sessions differ in everything about the document — empty vs. a real analyzed structure — and the failure set did not move by one endpoint.
- License tier is not. Same MIDASIT account, same license/edition, same
build. Per this file's own caveat below, that is not yet the "different
account" trigger, so
PRODUCTSwas left unchanged for the 7 Gen-fails/Civil-succeeds classes. This section is the second data point; a third from a different account or license would be the one to act on. - Results recorded per endpoint in
docs/coverage.json(live_verified), taking the count from 10/390 to 235/390.
Hyper-S is Civil NX only — the SDK's PRODUCTS was wrong¶
Running both products the same day made this unmissable:
Hyper-S (-M1) endpoint family |
Gen NX 2026 v2.1 | Civil NX 2026 v2.1 |
|---|---|---|
Implemented -M1 endpoints |
13/13 → 404 | 13/13 → answered |
Undocumented -M1 stubs |
8/8 → 404 | 8/8 → answered |
/db/ACTL-M1 returned a populated row under Civil, not just an empty table,
so these are live routes rather than registered-but-inert ones.
Confirmed by the project author: Hyper-S is the name MIDASIT gave the
solver introduced with the Civil NX release. It is a Civil NX product
feature, so the Gen 404s were correct all along and the SDK's
PRODUCTS = {"gen", "civil"} was the defect. All 21 -M1 rows (13
implemented + 8 stubs) are now Civil-only, via a dedicated HYPER_S_ONLY
constant in db/base.py and a parametrized guard in
tests/db/test_hyper_s_products.py.
This one is expected to change again. The author notes Hyper-S may reach
Gen NX in a future release. That is why it is its own constant rather than
CIVIL_ONLY: when it happens, widen HYPER_S_ONLY and re-run
scripts/live_readonly_sweep.py --product gen to confirm, instead of
treating the current state as permanent.
Note this changes the Gen failure accounting for the better. Of the 20 Gen
404s, 13 were never SDK-callable errors at all — they were Civil-only
endpoints the SDK wrongly offered to Gen clients. With PRODUCTS corrected,
a Gen sweep covers 240 endpoints with 7 unexplained 404s (CMCS,
EWSF, PLCB, RCHK, SPAN, STRPSSM, WVLD), and a Gen client now
raises ProductMismatchError before issuing a doomed request.
The 8 "un-transcribable" Hyper-S stubs are introspectable after all¶
docs/coverage.json carries 8 -M1 rows as planned, not implemented,
because the manual gives a URL, methods and a Zendesk link but no JSON
Schema — and this project's v1.0.0 gate has been waiting on exactly that.
Against a live Civil session, all 8 answer a plain GET, and 5 return a
field-level schema from /info/db/...:
| Endpoint | /info/db/... |
Top-level fields |
|---|---|---|
/db/STYP-M1 |
schema | STYPE, GRAV, TEMP, ALIGNBEAM, ALIGNSLAB, MASS_CONTROL |
/db/MATL-M1 |
schema | MATL_NAME, MATL_TYPE, DAMP_RAT, PARAM |
/db/IMFM-M1 |
schema | CONCRETE, STEEL |
/db/EPMT-M1 |
schema | NAME, MODEL_TYPE, TRESCA, VMISES, MOHRCL, DRUCKER, MASONRY, CONCDMG |
/db/IEHG-BEAM-M1 |
schema | INEL_PROP_NAME |
/db/IEHG-TRUSS-M1 |
404 | GET works, no schema route |
/db/IEHG-GL-M1 |
404 | GET works, no schema route |
/db/IEHG-PSS-M1 |
404 | GET works, no schema route |
/db/STYP-M1 and /db/MATL-M1 also returned real data ({"STYPE": "3D",
"GRAV": 9.806, ...}, {"MATL_NAME": "C24", "MATL_TYPE": "CONC", ...}).
This does not make them transcribed — server introspection is a different
source from the manual, and DbResource.info()'s docstring already frames it
as a fallback rather than a substitute. But the v1.0.0 gate's premise, that
these are "genuinely not transcribable without depending on an external,
non-versioned source", no longer holds: the server itself is the source, and
it is the same server the SDK talks to. Deciding whether that clears the gate
is a call for the author, not something this file should assume.
The {"message": ""} zero-row shape is the common case, not an edge case¶
Of the 233 endpoints that answered, 176 returned {"message": ""} and only
57 returned the keyed {"<KEY>": {...}} shape — on a fully populated model.
A zero-row table is the normal state for most endpoints because a given model
uses a small fraction of the API surface. This matters more than it looks:
DbResource.items() assumed the keyed shape and raised AttributeError on
the string value, so before v0.12.0 that helper failed on roughly three
quarters of the endpoints it was supposed to serve. Fixed in v0.12.0.
/post/TABLE's unstable top-level key, explained¶
Earlier sessions recorded the response key varying between TABLE_NAME,
"Result Table", and "empty" with no known trigger. It is simply the
default: omit or blank TABLE_NAME and the server keys the response
"empty"; pass a name and you get that name back. Same call, same 78 rows
of real data, both ways:
get_story_drift_table(STORY_DRIFT_X) -> keys=['empty'] rows=78
get_story_drift_table(STORY_DRIFT_X, table_name="Drift") -> keys=['Drift'] rows=78
"empty" is not an error marker — it carried a full table here. Matching on
shape (post.base.unwrap_table(), v0.12.0) remains the right approach, since
this doesn't explain the "Result Table" sighting, but "empty means no data"
is a wrong inference to guard against.
HTTP 200 with an error body — reproduced deliberately¶
Sending an unknown TABLE_TYPE returns HTTP 200 with:
This is the failure mode the client silently returned as a result until
v0.12.0; it now raises MidasResultError. Confirmed against the live server,
not just reasoned from the docs.
Two practitioner traps found while probing¶
- An invalid load-case name is not an error.
get_reaction_table( load_case_names=["NoSuchCase(ST)"])returned{"message": ...}with zero rows — after 23 seconds. Nothing distinguishes "wrong case name" from "genuinely no results" in the response, so validate case names against/db/STLDbefore trusting an empty table. - Unfiltered result tables can exceed a sane timeout. The beam-force
table for all 1272 elements timed out at 30s; the same table filtered to a
single load case returned 6350 rows in 2.2s. The displacement table came
back at 4.1 MB / 39760 rows. Pass
load_case_namesrather than raising the timeout.
2026-07-26 — write verification on Civil NX, and a table-destroying bug¶
First session with permission to create, modify and delete freely on Civil NX
2026 (v2.1), build 06/05/2026. scripts/live_crud_check.py was written for it:
create → read back → update → read back → delete → confirm gone, per resource.
Read sweeps prove an endpoint answers; only this proves the SDK's write
shapes are the ones the server accepts.
🛑 DbResource.delete([id]) was deleting the entire table¶
The single most serious defect found so far, and it was invisible to every
mocked test in tests/.
For /db/NODE this also takes out every element attached to those nodes, so
one delete() call empties a model. It surfaced by accident: the CRUD run
deleted one throwaway node, and the seeded model was gone afterwards.
The SDK was following the manual exactly. 03_DB_Node_Element.md documents
DELETE /db/NODE with {"Assign": {"4": None}}, and db/base.py had reasoned
carefully about None vs {} across chapters. Both forms behave the same way
live, and neither one respects the ids:
| Request | Result |
|---|---|
DELETE /db/NODE + {"Assign": {"3": null}} |
table emptied |
DELETE /db/NODE + {"Assign": {"3": {}}} |
table emptied |
DELETE /db/NODE + {"Assign": {"2": {}, "3": {}}} |
table emptied |
DELETE /db/NODE/3 |
only node 3 removed, and returned |
The per-id URL is undocumented — db/base.py explicitly declined to invent it
("no documented per-ID URL filtering across the manual, so we don't invent
one"). It works, and it is the only form that does what delete() claims.
Verified on /db/NODE, /db/STLD, /db/LDGR and /db/MATL; deleting an id
that doesn't exist is a harmless no-op returning {"message": ""}.
Fixed in v0.14.0: delete() issues one DELETE {endpoint}/{id} per id, and
the whole-table behaviour is kept under the name delete_all().
Two more "a 200 is not success" variants¶
v0.12.0 taught the client to reject a 2xx carrying {"error": ...}. Writing
turned up two cases that slip past it:
/doc/ANALreports a failed solve as{"message": "MIDAS CIVIL NX Analysis failed."}— the samemessagekey a successful call uses for"... command complete", with noerrorobject anywhere. Every result table then comes back empty with nothing explaining why.doc.analyze()now inspects that message; the check is deliberately not in the client, sincemessageis the normal success carrier elsewhere./doc/SAVEASreturns{"message": "... command complete"}for a save that did not happen. Given a path NX rejects it raises a modal "잘못된 경로가 있습니다" dialog, blocks the session until someone clicks it, and then answers with the success string. No file on disk. There is no way to tell success from failure from the response — check the filesystem.
Note also that error bodies arrive under 201 as well as 200
(POST /db/CONS → 201 + {"error": ...}), so any check keyed on the status
being exactly 200 misses them.
verify_connection() cannot detect a blocked session¶
While a dialog was up, /mapikey/verify answered {"status": "connected"}
in milliseconds while every /db/* call timed out. The health check is served
by the relay, not by the blocked application. It tells you the process is alive
and the key is valid — it does not tell you the app can currently answer.
Field-level findings from the round trips¶
/db/CONS'sCONSTRAINTmust be exactly 7 characters. Six is rejected outright; eight is accepted and silently truncated to seven, giving a support that was never requested with no error raised./db/STLDignores the"Assign"key and renumbers. Posting under key7produced record2(NO: 2), the next free slot./db/NODEhonours the key exactly (posting under77yields node77). So the ID-keyed convention is not uniform, and code that writes a record and then reads it back by the key it sent will miss for load cases.create()is not an upsert. Re-posting an existing key returns201with{"error": ...}"Key Already Exist".
Round-trip results: Civil 10/10, Gen 9/9 (/db/MVCD is Civil-only and
correctly skipped there) across /db/GRUP, /db/BNGR, /db/LDGR,
/db/NODE, /db/SKEW, /db/STLD, /db/CNLD, /db/BMLD, /db/CONS and
/db/MVCD — all create/read/update/delete once the payloads above are right.
The four that failed on the first run were all bad test fixtures (deleted
prerequisites, a 6-character constraint), not SDK defects; that is worth
stating plainly, because a checker that cries wolf gets ignored.
The Gen run doubles as confirmation that the per-id delete fix works there
too, without needing a separate assertion: the /db/CNLD, /db/BMLD and
/db/CONS cases attach to seeded node 1, element 1 and node 2, and they run
after the /db/NODE case deletes node 101. Under the old whole-table
delete, that one call would have taken the seed with it and all three would
have failed — which is exactly how the bug was found on Civil.
2026-07-26 — /doc/NEW killed Gen NX once, and nothing explains it¶
Read the follow-up sections before acting on this one. The framing here — that the old
doc/newconcurrency trigger had returned — did not survive testing, and every hypothesis raised below was subsequently eliminated, including by reopening the very model involved. The crash is real and the mitigation stands; the cause is unidentified.
A single /doc/NEW against Gen NX 2026 (v2.1), build 06/23/2026 with a
real 710-node / 1272-element analyzed model open produced the license
work-session crash dialog and killed the application:
[Error] Failed to disconnect the work session due to an unidentified error.Since you have not logged out, other PCs may have limited access to thelicense. In order to properly terminate the program, try to re-execute theprogram, press 'New Project' and then close the program.
The API side reported it correctly and immediately: POST /doc/NEW -> 404:
Client Disconnected, surfaced as MidasNotFoundError. No hang, no timeout —
the SDK's only involvement was issuing one documented call.
This reopens a trigger this file had closed. The section "This exact crash
signature has a prior precedent" records that the doc/new trigger was
retested against v2.1, found no longer reproducible, and closed out as
"limited to a previous build or a specific sequence". That conclusion is now
wrong twice over: it was already questioned by the *-ANAL reproductions, and
here the original doc/new trigger itself reproduces on the current build.
Two things are different from the earlier doc/new reproductions, and both
make this worse, not better:
- It was one call, not a burst. The earlier round needed back-to-back x10 or concurrent x5/x15 to trigger it. This was a single request.
- The document mattered.
/doc/NEWhad been called perhaps a dozen times earlier the same day against small scratch documents on Civil NX with no incident. The one that crashed was the first against a large, analyzed, loaded-from-disk model.
That points at document teardown of a real model, not at call frequency. It is
one occurrence, so treat the size/state correlation as a hypothesis worth
testing deliberately rather than an established cause — but the "resolved"
label on the doc/new trigger should not be restored without a reproduction
attempt on a comparable model.
Practical consequence, unchanged from the earlier findings: once this
dialog appears the program always dies, and the license stays checked out
until the process is properly terminated — which per the dialog means
re-running the program, pressing New Project, and closing it cleanly. Do not
point /doc/NEW at a session holding a model that matters.
2026-07-26 — narrowing the /doc/NEW crash, and /doc/SAVEAS looks broken¶
What the /doc/NEW crash is not¶
Four controlled runs against Gen NX 2026 (v2.1, build 06/23/2026), each building the document through the API so only one variable moves:
Document under /doc/NEW |
Result |
|---|---|
| 2-node scratch | ✅ 3.1s |
| 710 nodes / 1339 elements, API-built | ✅ 3.6s |
| 750 nodes / 1300 elements, API-built, solved, reactions readable | ✅ 4.1s |
| 710 nodes / 700 elements, saved then reopened from disk | ✅ 4.1s |
| 710 nodes / 1272 elements, the user's real model, opened from disk, solved | 🛑 crash |
Four candidate causes tested and all four eliminated: the product being
Gen, model size, the presence of analysis results, and the document being
disk-backed. The last of those was the leading hypothesis after the first
three fell, and it did not survive contact with a controlled test — a document
saved via /doc/SAVEAS and reopened via /doc/OPEN tore down cleanly.
What still separates the one document that crashed:
- Content, not size. It carried 60 sections, 3 materials, structure and boundary groups, story data, load combinations, rebar data, and 190 design members each for RC/steel/SRC. Every test model had one material, one section and no design data at all.
- Session history. That session had served two full 253-endpoint read sweeps plus dozens of ad-hoc probes over several hours before the crash. The clean runs were minutes old.
Both are testable; neither is cheap, since each attempt costs a crash and a license recovery. Recorded as the two surviving hypotheses rather than picked between.
The session hypothesis, and why it is now the leading one¶
The project author proposed a mechanism for the second one that fits the evidence better than anything model-side: the server retains a record of the existing session, and a later request is read as a second client connecting, so the session/licence layer refuses the teardown. Three things support it:
- The dialog only ever talks about sessions and licences. It says nothing about the model — which is what you would expect given that four model-side variables have now been eliminated by experiment.
- The original documented trigger was literally concurrency. The earlier
pre-v2.1 round reproduced this crash with
doc/newcalled concurrently x5/x15. That was filed here as an obsolete build-specific quirk; under this hypothesis it is the same defect seen from the other end, and the "resolved" label was hiding a mechanism rather than a fixed bug. - NX runs on a separate machine, reached through MIDASIT's relay, so there really is distributed session state to get out of sync — this was not understood when the earlier notes were written.
What does not yet fit: Gen and Civil were both connected on the same account
(sjj0507@midasit.com, distinct connectionIDs) during the four clean
/doc/NEW runs as well as the crash, so two products sharing an account is
not sufficient on its own.
The one session-level difference that survives: the crashed session was the original long-lived connection — hours old, two 253-endpoint sweeps and dozens of probes deep — while every clean run happened on a freshly restarted NX process minutes old.
Tested: the historical concurrency trigger does NOT reproduce¶
Run against a trivial empty document on the same build, reproducing the pre-v2.1 pattern exactly, each phase followed by a health check:
| Phase | Result |
|---|---|
10 back-to-back /doc/NEW, no pause |
10/10 fine |
5 concurrent /doc/NEW, separate clients |
5/5 fine (18.6s) |
15 concurrent /doc/NEW, separate clients |
15/15 fine (59.5s) |
Session stayed healthy throughout, same connectionID, app responsive after
every phase. So concurrency alone does not do it, and the earlier round's
doc/new-burst trigger really does look fixed on v2.1 — the "resolved" label
was right about that pattern, even though the endpoint can still crash.
This corrects the framing in the section above, which read the single-call crash as evidence that the concurrency trigger had returned. It had not. What they share is the endpoint and the crash text, not the route to it.
Where that leaves the two hypotheses: the session-age one is weakened (an
idle-but-old session is still untested, but burst load on a fresh one is
clean), and model content is now the leading candidate by elimination.
Every clean run — including 30 /doc/NEW calls across these phases — was
against a document with one material, one section and no design data.
Tested: the model is innocent, and so is session load¶
The decisive test was run — the user reopened the exact model that crashed, on the same build, same account, same machine:
nodes 710 · elements 1272 · materials 3 · sections 60 · constraints 44
load cases 5 · load combos 6 · structure groups 3
RC design members 190 · beam rebar 31
/doc/NEW -> {"message": "MIDAS GEN NX command complete"} (3.7s)
session survived
No crash. Model content is eliminated too.
Session load was then tested on its own: two full read sweeps back to back
(~480 calls) followed by /doc/NEW — the shape of what preceded the original
crash. Also clean, 4.5s.
So every variable isolated so far is eliminated:
| Hypothesis | Verdict |
|---|---|
| Product being Gen | ✗ |
| Model size | ✗ |
| Analysis results present | ✗ |
| Document loaded from disk | ✗ |
| Request concurrency (10 serial / 5 / 15 parallel) | ✗ |
| Model content (the very model that crashed) | ✗ |
| Accumulated session load (~480 calls) | ✗ |
What this most likely is¶
This now matches the pattern already recorded in this file for CC-ANAL /
BC-ANAL: reproducible five times out of five, then clean on the same build
with nothing changed. Two different endpoints, the same crash text, the same
inability to pin a trigger. Reading them as one intermittent defect in
session teardown explains more than any per-endpoint story does — and it
is consistent with everything eliminated above, all of which is about the
document rather than the session.
Untested combinations remain (rich model and heavy session load together — yesterday's actual state; or wall-clock session age measured in hours rather than call count). But the useful conclusion for this SDK does not depend on resolving that:
- Treat
/doc/NEWand*-ANALas calls that can kill the application, regardless of what the document holds. There is no precondition to check. - The mitigation is unchanged and is not going to improve: don't point them at a session holding work that isn't saved, and expect to restart the product and recover the licence when it happens.
- For a vendor report, the honest framing is a class of intermittent
session-teardown failure across
/doc/NEWand*-ANAL, with this file's elimination table attached to show what it is not. That is more useful than a steps-to-reproduce list nobody can run.
🧭 Every path in this API belongs to the machine running NX, not yours¶
The single most useful thing learned today, and it cost five failed
/doc/SAVEAS attempts to notice.
The API is reached through MIDASIT's relay at moa-engineers.midasit.com, so
the NX process answering your calls may be on a different computer entirely
— it was here. Every path-valued field is therefore resolved on that machine:
/doc/SAVEAS,/doc/OPEN,/doc/IMPORT*,/doc/EXPORT*EXPORT_PATHon every/post/TABLEcall and every design report/image call- image capture paths in
/view/*and/ope/*
Writing to C:/Users/<your-windows-account>/Documents/... fails when that
account exists only on your machine. The failure is quiet in the worst way:
| Path sent | Response | What actually happened |
|---|---|---|
C:/Users/Dennis/Documents/x.mgbx (local account) |
command complete, 58.2s |
modal "invalid path" dialog on the NX machine, blocking until dismissed |
C:/Users/sjj0507/Documents/x.mgbx (NX machine's account) |
command complete, 0.4s |
saved |
Both answers are byte-identical. The only signals are the latency — a
blocked dialog inflates it — and, decisively, whether the file is really
there. Checking os.path.exists() from the calling script proves nothing; it
is looking at the wrong filesystem. /doc/OPEN on the path you just wrote is
the check that works, since it asks the same machine.
The manual repo's examples/javascript/auto-save-before-analysis.html already
warns about this — "%USERPROFILE% 같은 환경변수는 MAPI 서버(Gen NX 프로세스)가
인식하지 못합니다" — and derives the folder from the account name in
/mapikey/verify's user email (sjj0507@midasit.com → sjj0507). That is
the right pattern: ask the server who it is, then build the path.
Two corrections to earlier drafts of this file, kept because the reasoning error is instructive:
/doc/SAVEASis not broken. It was writing files correctly the whole time, to a machine this script could not see.- Its response is still not proof of success — but the reason is the dialog, not the endpoint.
Same lesson for .mgbx: the manual's example still shows the pre-NX .mcb,
and Gen NX 2026 writes .mgbx. That turned out not to be what was failing
here, but the example extension is stale regardless.
2026-07-26 (later) — 🛑 POST /db/NMAS kills Civil NX, deterministically¶
The first crash in this file with a reproducible one-call trigger. Read it
alongside the /doc/NEW section above, whose "cause unidentified, survives
every hypothesis" conclusion it partly supersedes: the license dialog those
sections treat as an unexplained session-teardown symptom can now be produced
on demand.
Civil NX 2026 (v2.1), build 06/05/2026. Four reproductions, no exceptions.
The call times out, every subsequent /db/* call times out, and the
application raises
[Error] Failed to disconnect the work session due to an unidentified error. Since you have not logged out, other PCs may have limited access to the license.
and exits — holding the license until the product is re-run, New Project is
pressed, and it is closed properly. A second dialog names it outright:
"Program will be closed due to an unexpected problem."
Where NX puts its crash-recovery file varies with the document, and one of the
two locations it chose is unusable. Twice it tried
C:\Program Files\MIDAS\MIDAS CIVIL NX\DgnPlugIn\_restore.mcb and was
refused, since a standard account cannot write there; on the fourth
reproduction it wrote to the user's Downloads folder successfully. So
auto-recovery is not universally broken here — but it fails silently in the
Program Files case, which is worth knowing before relying on it. That is an
install-level problem, separate from the crash.
The three reproductions, and why the third one settles it¶
| # | Preceding activity | /doc/NEW? |
Idle | Outcome |
|---|---|---|---|---|
| 1 | full core + boundary tiers; /db/SDSP create→update→delete completed immediately before |
yes | none | died on /db/NMAS |
| 2 | verify_connection, GET /db/NODE (10 nodes), GET /db/NMAS ({}) |
no | ~32 min | died on /db/NMAS |
| 3 | /doc/NEW, seed model, GET /db/NODE, control POST /db/CNLD (0.1s), GET /db/CNLD |
yes | none — 20s-old session | died on /db/NMAS |
| 4 | POST /db/NODE, POST /db/SKEW, POST /db/CONS, GET /db/NMAS, GET /db/NODE — five calls at 0.08–0.17s each, all within the 1.3s before |
no | none | died on /db/NMAS |
Two competing explanations were raised. Both are excluded by the table.
An idle work-session timeout, since a ~32-minute absence sat in front of reproduction 2. It does not survive the evidence.
- In run 2 itself, three
/db/*calls succeeded after the idle gap and one of them returned all ten nodes. Those are answered by the application, not the relay, so the work session was alive. - The narrower variant — "the first write after a long idle is what dies" —
is excluded by run 1, where
/db/NMASwas nowhere near the first write: 100+ calls including dozens of writes had already succeeded, and/db/SDSPhad just completed a full round trip. - Run 3 then removes the confound entirely: a 20-second-old session, a control write to a sibling static-load endpoint succeeding in 0.1s, then this one call taking the session down.
A blocking save-changes dialog, since /doc/NEW raises one on a document
with unsaved changes, and this file already records that any modal dialog
freezes the whole API session rather than the one call. Reproduction 4 was run
specifically to test it: no /doc/NEW anywhere in the run, so nothing
could raise that dialog, and it worked on node ids 9001/9002 so the open
document was left alone. Three writes and two reads returned in 0.08–0.17s
each inside the 1.3 seconds before the call — which also excludes a dialog
raised by anything else, because a modal would have frozen those five too.
POST /db/NMAS died exactly as before.
The dialogs that appear afterwards are consistent with the crash rather than an explanation of it: NX raises the license and "unexpected problem" dialogs on its way out.
GET /db/NMAS and /info/db/NMAS are unaffected. Nothing about the payload is
unusual — three unit masses on a plain seeded node — and /info/db/NMAS lists
exactly the fields being sent. This is a product defect, not a wrong request
shape in this SDK.
verify_connection() reports "connected" throughout¶
Worth restating because it was measured twice in a row here, 0.5s each time,
while GET /db/NODE timed out at 15s in between. The health check is served by
the relay. It confirms the key is valid and the connection record exists; it
tells you nothing about whether the application can answer.
What was done about it¶
NodalMassPayloadindb/static_loads.pycarries the warning, with the recommendation to useLoadsToMass(/db/LTOM) where the mass can be derived from loads instead.scripts/live_crud_check.pyquarantines the case:Case.crashesmarks it, it is skipped by default and reported asSKIP, and running it needs an explicit--include-crashers. It is also last in its tier, so opting in costs only that one case rather than the seven that sat behind it in run 1.- The checker now aborts the whole run the moment a failure looks like a lost
session (
client does not exist, or a read timeout) instead of grinding through the remainder. Run 1 spent two 30s timeouts and six 404s reporting "failures" against an application that had already exited.
Untested on Gen NX. Do not assume it is Civil-only, and do not assume it is version-specific on this evidence alone.
2026-07-26 (later) — write verification, tiers 2-6: 40 of 43 cases confirmed¶
scripts/live_crud_check.py grew from 11 cases to 43, grouped into six tiers
ordered by what a modelling script actually reaches for. Confirmed live on
Civil NX 2026 v2.1 the same day: 40/43, across five runs. The three that
are not confirmed each have a recorded reason, and none of them is an SDK
defect: /db/NMAS crashes the product, /db/TDMT refuses every payload
server-side, and /db/TMAT cannot be reached without /db/TDMT.
Superseded later the same day, and the numbers below are the v2.1 state, not the current one.
/db/TDMTwas not refusing anything server-side — theCODEvalue was wrong; it and/db/TMATboth round-trip, taking this to 42/43 on v2.2. See the two later sections.
Newly proven round trips (create → read → update → read → delete → read):
| Tier | Endpoints |
|---|---|
props |
/db/THIK /db/ESSF /db/SECF /db/TSGR /db/TDME |
boundary |
/db/NSPR /db/GSTP /db/GSPR /db/ELNK /db/RIGD /db/MCON /db/FRLS /db/OFFS /db/SSPS — 9/9 first time out |
static |
/db/SDSP /db/LTOM /db/NBOF /db/FBLD /db/PSLT /db/PRES /db/ETMP /db/NTMP — 8/8 once /db/NMAS was quarantined out of the way |
stage |
/db/STAG /db/TMLD /db/CRPC /db/CMCS |
moving |
/db/LLAN /db/MVHL /db/MVHC /db/MVLD |
Still unconfirmed: /db/NMAS (quarantined, above), /db/TDMT (below) and
/db/TMAT (blocked by /db/TDMT).
The quarantine paid for itself immediately: the seven static cases that had
never been reached — they sat behind /db/NMAS in the crashed run, and were
never evidence of anything — all passed once it was skipped and moved last.
Six on the first attempt, /db/PRES after the fix below.
🛑 /db/SECF is keyed by section id, and the SDK said element id¶
db/properties/section.py documented /db/SECF ("Section Manager -
Stiffness") as keyed by element id. It is keyed by section id.
| Request | Response | Stored |
|---|---|---|
POST /db/SECF {"Assign": {"3": {...}}} (element 3) |
200, no error | nothing |
POST /db/SECF {"Assign": {"1": {...}}} (section 1) |
200 | the record, echoed back in full |
The wrong-key form is silent — no error object, no message, nothing to notice except reading back and finding an empty table. Since this project's TypedDicts are explicitly documentation rather than runtime validation, a wrong comment here is the defect; corrected in the docstring, with the evidence.
The fixture found it on purpose: the case was deliberately keyed to element 3
with a note saying a "missing after write" failure would mean the key was a
section id, because the manual's worked example keys it 9001 right next to
/db/STRPSSM's 9003, and /db/STRPSSM is section-keyed.
⚠️ /db/MVHL silently downgrades a standard vehicle to a user-defined one¶
VEHICLE_LOAD_NUM must be 1 for a standard-DB vehicle. Send 2 and NX
discards VEHICLE_TYPE_NAME and STANDARD_CODE and stores a user-defined
"Truck/Lane" vehicle instead, with a 200 and no error:
sent {"MVLD_CODE": 6, "VEHICLE_LOAD_NAME": "KR(SRB)_DB-18",
"VEHICLE_LOAD_NUM": 2, "VEHICLE_TYPE_NAME": "DB-18",
"STANDARD_CODE": "KS-RB", "VEH_DEFAULT": {"DYN_LOAD_ALLOWANCE": 0, ...}}
stored {"MVLD_CODE": 6, "VEHICLE_LOAD_NAME": "KR(SRB)_DB-18",
"VEHICLE_LOAD_NUM": 2, "USER_LOAD_TYPE": "Truck/Lane",
"VEH_DEFAULT": {"UNIFORM_LOAD": 0, "PL": 0, "PLM": 0, "PLV": 0}}
With VEHICLE_LOAD_NUM: 1, DB-18, DB-24 and DL-24 all store correctly
under STANDARD_CODE: "KS-RB". This joins /db/CONS truncating an 8-character
CONSTRAINT and the empty-VEH_DEFAULT no-op as a silent data-corruption
shape: the only defence is to read the record back and compare.
Also resolved: /db/MVHC's VEHICLE_LD_NAMES takes the vehicle's
VEHICLE_LOAD_NAME ("KR(SRB)_DB-24"), not the type name ("DB-18") the
manual's worked example shows.
⚠️ /db/PRES's documented default DIRECTION is rejected¶
DIRECTION is documented (manual and SDK alike) as defaulting to "NORMAL".
On a 4-node PLATE with FACE_EDGE_TYPE: "FACE" — the commonest pressure load
there is — Civil NX 2026 v2.1 rejects it:
Omitting DIRECTION fails identically, which confirms the default is
applied and the default is unusable. Working values on that same element:
DIRECTION |
Result |
|---|---|
LZ (element local z — normal to the plate), LX, GZ |
accepted |
VECTOR with VECTORS: [0, 0, -1] |
accepted |
NORMAL |
(Item:Load Direction) |
NORMAL_PLANE, NORMAL_ELEM, GLOBAL_Z, LOCAL_Z |
Wrong Field |
The two error strings separate the cases again: "NORMAL" earns the specific
"Load Direction" complaint rather than the generic Wrong Field that the
invented spellings get, so it is a recognised enum value — presumably valid
for some other ELEM_TYPE/FACE_EDGE_TYPE pair. For plate faces, pass "LZ".
Two smaller notes from the same endpoint: the server echoes FORCES back with
five entries where the manual's example sends four, and /info/db/PRES gives
its maxItems as 5; and /info/db/PRES carries a PSLT_KEY field (a
/db/PSLT reference) that the manual chapter does not document.
Also on /db/PSLT: the manual spells ELEM_TYPE "Plate/PlaneStress(Face)"
in its worked example and "Plate/Plane Stress (Face)" in its Specifications
prose. This section originally concluded the unspaced form was "the one the
server accepts" — that was wrong, and it was inferred from the CRUD case
passing with the unspaced form without ever sending the spaced one. Both are
accepted (checked on v2.2, see the final-verification section). The manual's
inconsistency is cosmetic.
⚠️ The manual's only documented time-dependent-material code name is rejected¶
"KDS2016" — the value in the manual's worked example, its Python example and
its NAME field for both /db/TDMT and /db/TDME — is not accepted by Civil
NX 2026 v2.1. Probed against /db/TDME:
CODENAME |
Result |
|---|---|
CEB-FIP(2010), CEB-FIP(1990), Ohzagi |
accepted |
ACI |
accepted once A/B are supplied |
KDS2016, KDS(2016), KDS, KCI-2007, Korea Standard |
Wrong Field |
KDS-2016 |
recognised, but still rejected even with A/B |
The two error strings are diagnostic and worth knowing:
"Wrong Field"— the code name is not one the product knows."[Error] Time Dependent Material(Comp. Strength) input data contain errors."— the name was recognised; the code's own conditional fields are missing.
⚠️ /db/TDMT looked server-side broken and was not — see the correction below¶
Recorded as written at the time, because the reasoning error is the useful
part. /db/TDMT answered 201 + {"error": {"message": "Wrong Field"}} to
the manual's worked example, to every code name /db/TDME accepts, to each
documented field removed in turn, and to a bare {"NAME": "C"}; PUT
behaved the same; /info/db/TDMT listed every field being sent; and an
{"Argument": ...} body was refused with a different, correct error. From
that this file concluded "looks server-side".
That conclusion was wrong. Nothing about it was server-side — the CODE value
was simply not one this endpoint accepts, and "Wrong Field" is what it says
when it cannot resolve CODE. Resolved further down: "Wrong Field" from
these endpoints means the code name is unknown, not that a field name is
wrong, and the message being about "Field" is actively misleading. The
lesson worth keeping: an exhaustive-looking elimination sweep over the fields
proves nothing when the bad value is in a field you never varied.
Fixture design rules, and why they earned their keep¶
Two rules, both paid for by the first run:
- Seed at the lowest free key, let the case take the next one. Definition
tables disagree about honouring the
"Assign"key —/db/NODEhonours it,/db/STLDand/db/TDMErenumber to the next free slot (posting/db/TDMEunder keys 10/15/16/18 produced ids 1/2/3/4). Seeding first and taking the next sequential key makes both behaviours land on the same id. Where a record is referenceable by name — groups, spring types, lanes, vehicles — the fixtures use the name and the question never arises. - Seed steps are per-case dependencies, not per-tier. The first run
blocked all 7
propscases behind the one/db/TDMTseed failure, though only/db/TMATneeded it. Four of those seven pass. A checker that reports 6 false blockages out of 7 gets ignored, which is the whole point of separating regression (a confirmed case broke → SDK defect suspect, exit 1) from unverified (never passed → triage the fixture, exit 3) from blocked and skipped.
The classification held up: across three runs every single failure resolved to
a fixture defect, a documented-value defect, or a product defect — and none to
an SDK behaviour defect. The one SDK defect found was a wrong docstring
(/db/SECF), which no amount of round-tripping would have caught if the case
had been keyed correctly by accident.
2026-07-26 (later still) — Civil NX v2.2: nothing changed¶
The user upgraded to MIDAS Civil NX 2026 (v2.2), build 06/18/2026 — a version bump, not a reinstall of the crashing build — and the whole checker was re-run against it.
The 40 confirmed round trips are unchanged¶
live_crud_check.py --product civil gave a byte-identical verdict on v2.2:
40/43, zero regressions, the same three exceptions. That is worth having
on its own — it is the first evidence in this project that the SDK's write
shapes survive a Civil NX version change, and the endpoints in
docs/coverage.json now cite v2.2 as their verified version. (The same 40
passed on v2.1 build 06/05/2026 earlier the same day; the nx_versions field
holds one build per product, so it names the newer one.)
🛑 /db/NMAS reproduction #5 — the upgrade did not fix it¶
Same protocol as reproduction #4, deliberately: no /doc/NEW anywhere in the
run, control writes immediately before, node ids 9001+ so the open document is
untouched.
[ 0.6s] OK POST /db/NODE 9001-9005 (fixture) (0.11s)
[ 0.7s] OK CONTROL POST /db/SKEW 9001 (0.11s)
[ 0.8s] OK CONTROL POST /db/CONS 9002 (0.11s)
[ 0.9s] OK CONTROL GET /db/NODE (t-0) (0.08s)
[ 15.9s] FAIL POST /db/NMAS 9001 (15.01s) read timeout
[ 31.4s] FAIL GET /db/NODE (15.47s) read timeout
The probe was written to keep going if the call had survived — five more
POSTs, a PUT, a DELETE and a read-back — precisely so that a clean
result would not rest on one lucky call. It never got there.
This is now five reproductions across two versions. It matters because of
the precedent this file already records: CC-ANAL/BC-ANAL were reproducible
five times out of five and then ran clean on the same build with nothing
changed, and /doc/NEW's crash never got a trigger at all. /db/NMAS is not
that. It is deterministic, it is one call, the payload is three unit masses on
a plain node, and a vendor version bump did not touch it.
Practical consequence: do not wait for this to be fixed by upgrading, and the vendor report is worth actually sending — see the crash section above for the evidence to attach.
/db/TDMT is not version-specific either¶
Still 201 + {"error": {"message": "Wrong Field"}} on v2.2, for the manual's
own worked example. Whatever is wrong with that endpoint survived the same
upgrade, which removes "stale build" as an explanation and makes it worth
reporting alongside the crash rather than sitting on it.
2026-07-26 (later still) — /db/TDMT solved: the code-name enums differ¶
/db/TDMT and /db/TDME sit next to each other in chapter 04, take a code
name each, and do not share a code-name enum. That is the whole answer, and
it cost most of a session to find because a Wrong Field error pointed at the
fields rather than at the value.
Probed live on v2.2 with the CEB-FIP field set (MSIZE/CTYPE) and the ACI
field set (VOL/CMETHOD), 16 candidate names each:
CODE on /db/TDMT |
Result |
|---|---|
European |
accepted with either field set |
AASHTO |
accepted with either field set |
ACI |
accepted with VOL/CMETHOD |
Russian |
recognised — "input data contain errors", so it wants other fields |
CEB-FIP, CEB-FIP(2010), CEB-FIP(1990), CEB-FIP(1978), CEB FIP |
Wrong Field |
Ohzagi, KDS-2016, KDS2016, Korea, KCI-USD12, JTG3362-2018 |
Wrong Field |
So MIDAS calls the CEB-FIP-based creep/shrinkage model "European" on this
endpoint, and every CEB-FIP spelling is rejected — while /db/TDME accepts
CEB-FIP(2010)/CEB-FIP(1990)/Ohzagi and rejects European-flavoured
guesses. The manual's chapter blurb for /db/TDMT — "CEB-FIP(2010/1990/1978),
ACI, KDS 등" — describes a set of names the endpoint does not take. Stored
records come back with CODE upper-cased ("European" → "EUROPEAN").
The two error strings are the same diagnostic pair seen on /db/TDME, and they
are the fastest way to triage this class of endpoint:
"Wrong Field"→ the code name is unknown. Vary the value, not the fields."[Error] ... input data contain errors."→ the name is recognised; the code's own conditional fields are missing or wrong.
With CODE: "European", /db/TDMT round-trips, and /db/TMAT — which links a
/db/TDMT and a /db/TDME record by name — round-trips after it. That
takes the checker to 42/43 confirmed, leaving only the /db/NMAS crash.
One more self-inflicted fixture defect on the way there, worth recording
because it is the exact rule this file lays out two sections up: /db/TMAT's
update pointed TDMT_NAME at TDMT_CRUD, the record the /db/TDMT case
creates and deletes, so the update earned a Wrong DB Name for a reason that
had nothing to do with /db/TMAT. Fixed by seeding two creep/shrinkage records
(TD_SEED, TD_SEED_2) that outlive the tier and switching between them.
What this changes about the vendor report¶
/db/TDMT comes off the product-defect list and moves to the manual team:
the endpoint works, its documented code names don't. What is worth reporting as
a product-side issue is the error text — "Wrong Field" for an unrecognised
value sends the reader to inspect field names, which is what happened here.
2026-07-26 (later still) — v2.2 re-check of the v2.1-only findings¶
Three findings had only ever been seen on v2.1 (build 06/05/2026) and were about to be sent to MIDASIT on that basis. Re-run on v2.2 (build 06/18/2026) before sending. Two hold, one does not.
| Finding | v2.1 | v2.2 |
|---|---|---|
/db/CONS silently truncates an 8-character CONSTRAINT to 7 |
reproduces | reproduces |
DELETE {endpoint} + ID-keyed "Assign" empties the whole table |
reproduces | reproduces |
/db/MVHL no-ops on an empty VEH_DEFAULT: {} |
reproduces | does not reproduce |
/db/CONS — and the response lies too¶
The truncation is unchanged, and re-checking it turned up something the original write-up missed: the POST response echoes the 8-character string back, while the stored record holds 7.
POST /db/CONS {"Assign": {"3": {"ITEMS": [{"ID": 1, "CONSTRAINT": "11111111"}]}}}
response -> {"CONS": {"3": {"ITEMS": [{"ID": 1, "CONSTRAINT": "11111111"}]}}} 8 chars
GET -> {"3": {"ITEMS": [{"ID": 1, "GROUP_NAME": "", "CONSTRAINT": "1111111"}]}} 7
So the immediate response cannot be used to detect it either — a separate GET is the only signal. Six characters is still rejected outright, so the asymmetry is the real complaint: too short errors, too long is silently cut.
DELETE — measured on v2.2, and it is as bad as recorded¶
before NODE 10, ELEM 4
DELETE /db/NODE {"Assign": {"21": null}} one node named
after NODE 0, ELEM 0 the model is gone
before STLD ['1', '2']
DELETE /db/STLD {"Assign": {"1": {}}} one load case named
after STLD []
DELETE /db/NODE/502 the per-id form
-> {"NODE": {"502": {...}}}, 501 and 503 untouched
One detail worth having: the response body returns every record it deleted,
so the response does reveal the over-deletion — after the fact. delete()
already uses the per-id URL, so nothing changes in the SDK.
/db/MVHL's empty VEH_DEFAULT — fixed, so it comes out of the report¶
On v2.2 the record saves and VEH_DEFAULT comes back populated with
{"DYN_LOAD_ALLOWANCE": 0, "CENT_F": false} — the product now fills the
defaults instead of discarding the write. Removed from the vendor report's
silent-write-failure list, since sending a claim that does not reproduce on the
current build costs credibility for the ones that do; kept in the report's
appendix as a note that it appears resolved, and the VehicleDefaultParams
docstring now scopes the warning to v2.1.
This is the argument for re-checking before reporting rather than after: the same sweep that confirmed two findings retired a third and sharpened one of the two.
2026-07-26 (final) — every vendor-report claim re-checked in one pass¶
Before sending anything to MIDASIT, all 16 claims were re-run against a single
v2.2 session (build 06/18/2026) in ascending order of severity — the
documentation items first, then the response conventions, then the silent
write failures, then the destructive DELETE, and the crash last. Ordering it
that way matters: the crash ends the session, so anything after it would go
unverified.
14 of 16 reproduced. The two that did not are the useful part.
❌ Retracted: /db/PSLT's ELEM_TYPE spelling was never a defect¶
Both "Plate/PlaneStress(Face)" and "Plate/Plane Stress (Face)" are
accepted. The earlier claim that only the unspaced form works came from the
CRUD case passing with that form — the spaced form was never sent. That is an
inference dressed up as a finding, and it is exactly the failure mode this file
already records for /db/TDMT: do not conclude anything about an enum from
one value working. Removed from the vendor report.
⚠️ A-4 failed its own check, not the claim¶
The assertion required two probes to both return an error body, and one of them
(POST /db/CONS with a 3-character CONSTRAINT on a node that does not exist)
returned 201 with no error at all. The claim itself is confirmed by the other
probe — POST /db/TDMT → 201 + {"error": {"message": "Wrong Field"}} —
and by PUT /db/TMAT → 200 + Wrong DB Name earlier the same day. Bad
test, good finding; the report's example list was corrected rather than the
claim.
That stray 201-with-no-error on a nonexistent node is itself suspicious as
another silent no-op, but it was not checked with a follow-up GET and is not
claimed anywhere.
/mapikey/verify is stale, not permanently wrong¶
Worth correcting because the report said it reports "connected" for a dead
application. It does — but not forever. Measured immediately after two crashes:
"connected". Measured after this crash, roughly 30 seconds later and after
two 15s timeouts had elapsed: "disconnected". So the relay's connection
record catches up; there is a window in which it lies. The report now says
that instead.
Confirmed unchanged on v2.2¶
/db/TDMT's code-name enum (B-1), /db/TDME's (B-2), /db/SECF's section
key (B-3), /db/PRES's rejected "NORMAL" default and its 5-entry FORCES
(B-4, B-7), /db/MVHC's VEHICLE_LD_NAMES — which rejects a vehicle type
name with Unknown Error (B-5) — /db/STLD's renumbering, where a POST under
key 7 produced id 3 (B-6), the "Wrong Field" vs "input data contain
errors" split (A-6), all three silent write failures (A-3a/b/c), DELETE
emptying /db/NODE from 10 nodes and 4 elements to zero from a single named id
(A-2), the per-id form working correctly (A-2b), and POST /db/NMAS killing
the application — reproduction #6 (A-1), with the three control calls
immediately before it all returning normally.
2026-07-27 — 🛑 four of the seven "documentation defects" were ours, not MIDASIT's¶
Before sending the vendor report, every B-item was re-checked against the
official Zendesk articles (support.midasuser.com, JSON Manual section
30087500371097) fetched that day — not against E:\AI Study\MIDAS-API.
That distinction is the whole finding. The vendored manual is a curated
transcription; section B had been written against it, so wherever the
transcription was wrong, we were about to accuse MIDASIT of our own error.
B-1 /db/TDMT CODE — retracted. The official article
(Creep/Shrinkage)
documents a complete, exact 28-value enum: CEB_FIP_2010, CEB,
CEB_FIP_1978, ACI, PCA, COMBINED, AASHTO, JSCE_12, JSCE_07,
JSCE, JAPAN, INDIA_IRC_18_2000, INDIA_IRC_112_2011, EUROPEAN,
AS_5100_5_2017, AS_5100_5_2016, AS_RTA_5100_5_2011, AS_3600_2009,
NEWZEALAND, RUSSIAN, CHINESE, JTG, CHINA_JTG3362_2018, KDS_2016,
KSI_USD12, KSCE_2010, KS, USER_DEFINED.
The 16 values swept on 2026-07-26 were NAME values read as CODE values.
In the official example, "NAME": "CEB-FIP(1990)" sits directly above
"CODE": "CEB" — NAME is the free-text label, CODE is the enum. Every
"rejected CEB-FIP spelling" recorded above was a string the official article
never proposes for that field. "European" was not a discovery; EUROPEAN
is documented, and the match is case-insensitive.
B-2 /db/TDME CODENAME — retracted. The official article
(Compressive Strength)
gives "KDS-2016". "KDS2016", the value probed and reported as rejected,
appears in neither official article — it is a vendored-copy error.
So the two endpoints do differ, and interestingly: /db/TDMT takes
UNDERSCORED_UPPERCASE tokens, /db/TDME takes the display string
(CEB-FIP(2010), KDS-2016, European). Both are documented correctly and
in full. That the SDK's own seeds already use CODENAME: "CEB-FIP(2010)" and
pass live is confirmation, not coincidence.
B-3 /db/SECF — retracted. The official article is titled Section
Manager - Stiffness and never states what the "Assign" key means; it just
uses 9001/9002 as example keys. "Keyed by element id" was our own
docstring, already corrected in db/properties/section.py. The live finding
(section id is the right key) stands; the accusation does not.
B-7 /db/PRES — retracted. The official Specifications row reads
"FORCES" Array [Number, 5], and all four official examples carry five
entries. PSLT_KEY is present in the official JSON Schema. Both halves
described the vendored chapter, not the source.
B-4 /db/PRES DIRECTION — narrowed, not retracted. Footnote ¹⁾ of the
official article carries an availability matrix that documents the live
behaviour exactly: for "PLATE" + "FACE", Normal is - while Local x/y/z,
Global X/Y/Z and Vectors are O. What survives is much smaller: the
Specifications row still marks DIRECTION Optional, default "NORMAL",
which cannot hold for the one combination where NORMAL is unavailable —
hence omitting the field fails.
B-5 and B-6 stand, B-6 rephrased as an omission: the official /db/STLD
article marks "NO" Read Only and never says what the "Assign" key does,
so renumbering contradicts no published statement — it is undocumented, which
is a weaker and more accurate claim.
The rule this produces¶
Never cite the vendored manual as "the documentation" in anything sent
outside. It exists to be corrected; E:\AI Study\MIDAS-API's own CLAUDE.md
says it deliberately normalizes official typos and marks them ⚠️. For an
internal decision it is the right source. For a claim about MIDASIT's
documentation, fetch the article and quote it. Four of seven claims died on
contact with the source, and the two that survived got weaker.
Still open: a live re-test with the official values (CODE: "CEB_FIP_2010",
"KDS_2016" on /db/TDMT; CODENAME: "KDS-2016" on /db/TDME). It was not
possible on 2026-07-27 — the relay answered client does not exist, no
session. The retractions do not depend on it (dropping an unverified
accusation is the safe direction), but if any official value does fail,
that is a genuine product defect and a new A-item.
2026-07-29 — Gen NX v2.1 (build 07/28/2026): props/boundary/static/stage get their first Gen CRUD run, /db/CMCS corrected to Civil-only¶
(Dated 2026-07-27 in an earlier draft of this section — wrong. This work
happened in the same conversation as the section above but after a real
multi-day gap; git's own commit timestamps settled it: 7f63ea6 and
456d4fa, the two commits from this session, are both stamped 2026-07-29.)
New Gen NX install, new MAPI key, scripts/live_crud_check.py --product gen
(no --include-crashers). Correction to get right before it goes stale:
this was not the checker's first exposure to Gen NX — the core tier's own
comment already says "the baseline proven live on 2026-07-26 (Civil 10/10,
Gen 9/9)", and that 2026-07-26 Gen pass is what let /db/TDMT and
/db/TDME inherit confirmed=True in the first place. What today actually
added: the props, boundary, static and stage tiers ran against Gen
for the first time (27 endpoints), on top of core repeating clean. Of
those, /db/TDMT//db/TDME are worth calling out specifically — they use
the seeds already fixed in the 2026-07-27 section above
(CODE: "European", CODENAME: "CEB-FIP(2010)"), so this is the first
live confirmation of those specific values on a Gen session, not just
Civil. /db/NMAS stayed quarantined; not run.
First pass: 36/38 confirmed cases passed. /db/CMCS came back a
REGRESSION — confirmed=True in the checker, but that flag was earned on
Civil NX runs only; the stage tier had never touched Gen before today.
Before treating it as an SDK defect: /db/CMCS is one of the 7 endpoints
already flagged twice in this file (2026-07-22, 2026-07-26) as "manual says
gen+civil, but 404s under Gen in practice" — and both those were GET-only
read sweeps, deliberately left unactioned pending a third, independent data
point per this file's own caveat.
This run is that third point, on a different session (new build, new day)
and with stronger evidence than either prior one — an actual POST
attempting to create a record, not just a GET against an empty table. Same
account as before (sjj0507@midasit.com), so it doesn't clear the
stricter "different account" bar floated in the 2026-07-26 section, but it
does satisfy the caveat's actual rule below ("different session" is listed
as sufficient), and three same-account reproductions across seven days and
three separate sessions is not a coincidence worth waiting out further.
Action taken: CamberConstructionStage.PRODUCTS changed from
{"gen", "civil"} to CIVIL_ONLY in db/construction_stage.py, the
live_crud_check.py case given products=("civil",) so a future Gen run
skips it instead of false-flagging, and its mocked test switched from
gen_client to civil_client. The other 6 endpoints in that original list
(EWSF, PLCB, RCHK, SPAN, STRPSSM, WVLD) were not exercised by
live_crud_check.py, so this run added no new evidence for them — but see
2026-07-29's read-only sweep below, which closed that gap the same day.
Re-run after the fix: 36/37, clean — 0 regressions, 0 unverified failures,
0 blocked, /db/CMCS correctly filtered out for a Gen client rather than
skipped-and-counted. docs/coverage.json updated in bulk for all 36 passing
endpoints: live_verified.products gained "gen",
live_verified.nx_versions.gen set to "MIDAS Gen NX 2026 (v2.1), build
07/28/2026", date bumped to 2026-07-29. /db/CMCS's own entry instead
dropped "gen" from top-level products (now ["civil"], matching
PRODUCTS) and its live_verified note records the three-session Gen 404
history.
2026-07-29 (later) — 🛑 POST /db/NMAS also crashes Gen NX: not a Civil-specific defect¶
With the user's explicit go-ahead, --include-crashers --tier static was run
once against the fresh Gen NX v2.1 (build 07/28/2026) session — the first
time this case has ever touched Gen. It crashed on the first attempt:
POST /db/NMAS -> 404: Client Disconnected (Hint: either the product isn't
connected ... or the id you asked for doesn't exist in the current model)
A follow-up GET /db/NODE a few seconds later confirmed the session was
actually gone, not just this one call rejected: 404 {"error": {"message":
"client does not exist"}} — the exact string Civil NX gives after this same
crash. The user restarted Gen NX, pressed New Project, and reconnected;
GET /db/NODE on the new session returned 200 {"message": ""} (empty
document), confirming recovery.
This settles the question left open in the 2026-07-26 Civil section: the crash is not a Civil NX peculiarity. Six reproductions on Civil (two versions) plus one on Gen (first-ever attempt, different build entirely, different product) is the same failure on both of MIDASIT's NX flagship products through what must be a shared write path. Not re-running this again — one clean reproduction on Gen matching Civil's exact signature is enough; further attempts just burn licenses for no new information.
Updated everywhere this was recorded as Civil-only: CLAUDE.md,
NodalMassPayload's docstring in db/static_loads.py, the crashes= note
on the case in live_crud_check.py. The vendor report's A-1 needs the same
correction before it's sent — "Civil NX 한정" understates it.
2026-07-29 (later) — full read-only re-sweep on the new Gen NX build closes the remaining 6¶
With the NMAS-on-Gen question settled, the user asked what else the new Gen
NX patch was worth re-checking. scripts/live_readonly_sweep.py --product
gen — the same read-only GET sweep from 2026-07-22 and 2026-07-26 — was
re-run against Gen NX 2026 v2.1, build 07/28/2026, a session and build
distinct from both prior ones.
239 GET-capable resources swept, 233 ok, 6 errors — the exact same 6, byte
for byte: EWSF, PLCB, RCHK, SPAN, STRPSSM, WVLD, all
MidasNotFoundError 404 with the identical hint text. No drift, no new
failures, no fixes from the patch. (CMCS wasn't part of this sweep at
all — it's correctly excluded now that its PRODUCTS is Civil-only.)
That is the third independent reproduction this file's own caveat asked for
on these 6 — same bar /db/CMCS cleared a few hours earlier, same account,
different session and build each time (2026-07-22, 2026-07-26, 2026-07-29).
Action taken: all 6 classes' PRODUCTS changed from {"gen", "civil"}
to CIVIL_ONLY — RebarCheckInput (db/design.py), PreCompositeSection
and WaveLoad (db/misc_loads.py), Span (db/project.py),
SectionStressPoints and EffectiveWidthScaleFactor
(db/properties/section.py) — each with the same dated docstring note as
CamberConstructionStage. Their 6 mocked tests switched from gen_client
to civil_client (they were asserting a request shape against a product
that, per this finding, can't actually take that request).
docs/coverage.json's top-level products for all 6 corrected to
["civil"] to match, and their live_verified.method notes now record the
three-session history the same way /db/CMCS's does.
This closes out the "20 Gen 404s" accounting from the 2026-07-26 section
above for good: 13 were Hyper-S (corrected in v0.13.0), and these final 7
(CMCS plus these 6) are now all confirmed Civil-only after three
independent sessions apiece. Nothing in that original list of 20 remains
unexplained.
2026-07-29 (later still) — /db/NMAS reproductions #2 and #3 on Gen NX: 9/9 overall, confirmed on a real production model¶
The user asked to go back to /db/NMAS one more time, specifically on Gen
NX. Two more reproductions followed, back to back, each requiring the full
license-recovery cycle (restart, New Project, close properly) before the
next could run.
Reproduction #2 (Gen, scratch document): scripts/live_crud_check.py
--product gen --tier static --include-crashers, same throwaway-model setup
as the first Gen crash. This time the script's own _session_lost check
caught it inline and aborted the run immediately, rather than needing a
follow-up probe:
FAIL /db/NMAS create=FAIL
!! ABORTED: the product stopped answering at /db/NMAS — MIDAS NX is hung or
gone, so nothing after this point was tested
error: "POST /db/NMAS failed: ... Read timed out. (read timeout=60.0)"
A 60s timeout on the call itself this time, rather than the first crash's
immediate 404 Client Disconnected — a different wire-level symptom, same
outcome.
Reproduction #3 (Gen, real production model): the user opened an actual
work file — not a scratch document — and asked to GET /db/NMAS against it
first (safe; returned {"message": ""}, no masses defined). Then,
explicitly confirming the model was already saved, asked to POST it.
Rather than risk the model's own node numbering, this run reused the
established protocol from docs/vendor_repro_nmas.py (control writes/reads
immediately before the target call, node ids 9001+ so nothing in the real
model is touched, no /doc/NEW anywhere):
[ 1.0s] 201 POST /db/NODE 9001,9002 (0.56s)
[ 1.4s] 201 POST /db/SKEW 9001 (0.47s)
[ 1.9s] 201 POST /db/CONS 9002 (0.49s)
[ 2.3s] 200 GET /db/NMAS (target table) (0.41s) {"message": ""}
[ 2.7s] 200 GET /db/NODE (0.40s)
[ 18.1s] TIMEOUT POST /db/NMAS 9001 (15.35s)
[ 18.5s] 404 GET /mapikey/verify (0.39s)
[ 33.8s] TIMEOUT GET /db/NODE (15.34s)
Same shape as every prior reproduction: three control calls immediately before the target call all returned normally in under 0.6s each, the target call itself timed out, and everything after was dead. This is the first Gen NX reproduction against real production data, which matters the same way the Civil real-model evidence did earlier in this file: it rules out "only happens on synthetic/throwaway test data" as an explanation. The user had saved the model beforehand, so the crash cost a restart cycle, not work.
Running total after this section: 9/9 — six on Civil NX, three on Gen NX,
zero survivals. Every file that recorded this as "Civil NX" or cited a
specific reproduction count has been updated: CLAUDE.md,
NodalMassPayload's docstring in db/static_loads.py, the crashes= note
in live_crud_check.py. The vendor report's A-1 gets this as its strongest
piece of evidence yet — see below.
2026-07-29 (still later) — /db/NMAS reproduction #10, on a freshly updated Civil NX build¶
The user installed a new Civil NX build the same day — v2.2 (build 07/28/2026), one build newer than the v2.2 (06/18/2026) build already on record — and asked for a fresh round of checks before trusting it, ending with a repeat of the NMAS reproduction to see if the build update happened to fix it.
Read-only sweep first (293 GET-capable Civil resources, 273 ok / 20 error).
All 20 errors were 404, on both GET and the schema-only /info/db/...
probe (a control call, Node.info(), succeeded on the same session) — that
rules out "no data in this model" as the explanation, since a schema probe
doesn't touch model data at all. The 20: /db/STOR, /db/SWIND,
/db/SSEIS, /db/POSP, /db/EPST, /db/DRLS, /db/SDHY, /db/SDIS,
/db/REBB, /db/REBR, /db/REBW, plus 9 design-chapter endpoints —
/DESIGN/RC/KDS-41-20-2022/{MATD,REBB,REBC,REBR,REBW,TRFT,ULCT},
/DESIGN/SRC/AIK-SRC2K/MATD, /DESIGN/STEEL/KDS-41-30-2022/ULCT. 11 of
these had previously been live_verified as ok specifically on Gen NX
(2026-07-26) and had never been probed live on Civil before — so this reads
as a first-time signal that these may actually be Gen-only (the SDK
currently declares PRODUCTS={"gen","civil"} for all 20), not a build
regression; coverage.json shows no prior Civil live_verified entry for any
of them. Not acted on — per the caveat below, one session's evidence isn't
enough to flip a PRODUCTS frozenset; the trigger to revisit these 20 is an
independent reproduction (different session/account, or a fresh Civil
document via live_crud_check.py's throwaway-model setup) confirming the
same 404s. Separately, scripts/check_manual_drift.py found the
vendored manual repo two commits stale — the Story Drift X_DIR/X-DIR
contradiction (21_POST_StoryTables.md) was officially corrected by the
vendor (unified on the underscore spelling, type confirmed Object), so
post/story.py's StoryDriftVerticalLine(s)Selection TypedDicts and their
tests were updated to match and the hyphen fallback was dropped;
vendored_at_commit bumped to aeca67553dd078e2057f2ab5e87fe3391775e6ac
and re-confirmed has_diff: false. The other stale file
(04_DB_Properties.md's TDMT/TDME KDS2016 correction) needed no code
change — the SDK already had the corrected values. Then
scripts/live_crud_check.py --product civil (all six tiers, /db/NMAS
auto-skipped): 42/42 non-quarantined cases passed, matching the existing
42/43-confirmed baseline with zero regressions from the build update.
Finally, with the user's explicit go-ahead, --tier static
--include-crashers:
PASS /db/ETMP create=ok read_back=ok update=ok read_updated=ok delete=ok read_deleted=ok
PASS /db/NTMP create=ok read_back=ok update=ok read_updated=ok delete=ok read_deleted=ok
FAIL /db/NMAS create=FAIL
!! ABORTED: the product stopped answering at /db/NMAS — MIDAS NX is hung or
gone, so nothing after this point was tested
error: "POST /db/NMAS failed: ... Read timed out. (read timeout=60.0)"
Same shape as every prior reproduction: a plain throwaway model, seven calls
that all passed immediately before it, then a 60s timeout on the POST
/db/NMAS call itself and the session gone. The 07/28/2026 Civil build
does not fix this.
Running total after this section: 10/10 — seven on Civil NX (across three
version/build combinations), three on Gen NX, zero survivals. Updated the
same three files as the prior count bump: CLAUDE.md, NodalMassPayload's
docstring in db/static_loads.py, the crashes= note in
live_crud_check.py.
2026-07-29 (later yet) — /db/NMAS reproduction #11: a from-scratch minimal model rules out the "floating substructure" hypothesis¶
Before reproduction #10 (above), the user raised a legitimate structural-
engineering objection: every single reproduction to date had written the
mass to, or alongside, a node with no restraint and no connecting element —
vendor_repro_nmas.py's node 9001 gets a SKEW (a local-axis rotation, not
a restraint) but no CONS and no element, so it is a literally free-
floating point in space; live_crud_check.py's seed model targets node 3
(which is connected, via the beam chain back to node 1's fixity), but the
same model also permanently carries an entirely unrestrained, disconnected
plate (nodes 5-8) and, during the static tier, an unrestrained floating
node pair (21/22) left over from the boundary tier. A structural model
with an unrestrained, disconnected substructure has a singular stiffness
matrix (rigid-body modes with no associated stiffness) — if POST
/db/NMAS triggers any internal mass-matrix or modal bookkeeping, an
iterative solver hitting that singularity hanging instead of erroring out
cleanly is a real, previously-documented class of defect in commercial FEA
software. This was a serious enough alternative explanation to test before
trusting the vendor report's framing.
Test: build a deliberately minimal, structurally clean model directly
on the (already-blank, from the prior reproduction) open document — no
/doc/NEW — with nothing floating anywhere: node 1 (fixed, CONS
"1111111"), node 2 (free), one BEAM element connecting them, one
material, one section. The mass target, node 2, is unrestrained itself but
fully connected via the beam — a textbook, kinematically determinate
cantilever, not a mechanism, and the only thing in the document.
[ 0.7s] OK Unit (0.29s)
[ 0.8s] OK Material (0.12s)
[ 0.9s] OK Section (0.10s)
[ 1.0s] OK Node 1,2 (0.15s)
[ 1.2s] OK Element (beam 1-2) (0.16s)
[ 1.3s] OK Constraint (fix node 1) (0.12s)
[ 1.4s] OK GET /db/NMAS (target table) (0.09s) {"message": ""}
[ 1.5s] OK GET /db/NODE (0.08s)
[ 48.3s] ERROR POST /db/NMAS node 2 (46.85s) 404: Client Disconnected
[ 48.3s] OK GET /mapikey/verify (0.01s)
[ 48.3s] ERROR GET /db/NODE (0.01s) 404: client does not exist
Died identically. The user watched it happen on screen this time (a
first for this investigation) and captured the exact sequence: the
in-app dialog read verbatim [Error] Failed to disconnect the work session
due to an unidentified error. Since you have not logged out, other PCs may
have limited access to the license. In order to properly terminate the
program, try to re-execute the program, press 'New Project' and then close
the program. — matching every prior paraphrase of this dialog exactly —
followed by a second, previously-undocumented dialog: Program will be
closed due to an unexpected problem. The recovered file is saved in
[C:\Program Files\MIDAS\MIDAS CIVIL NX\MIDAS CIVIL NX\DgnPlugIn\_restore.mcb],
itself followed by an access-denied error for that exact path — the
crash-recovery autosave attempted to write under Program Files and was
denied, the same class of unchecked-write-permission issue as A-7 in the
vendor report (a different endpoint's report path), now also seen in the
crash handler itself. The model tree, visible on screen right before the
crash, confirmed the model was built exactly as intended: 2 nodes, 1 beam
element, 1 material, 1 section, 1 support — nothing else.
This rules out model topology as an explanation. A model with no
disconnected or unrestrained component anywhere, built from exactly the
fields the manual documents, dies the same way as every messier fixture
before it. The defect is in /db/NMAS's write handling itself, not
triggered by a singular stiffness matrix elsewhere in the document.
Running total after this section: 11/11 — eight on Civil NX (three
version/build combinations, one of them this minimal-model run), three on
Gen NX, zero survivals. Updated CLAUDE.md, NodalMassPayload's
docstring in db/static_loads.py, the crashes= note in
live_crud_check.py, and docs/vendor_report_ko.md's A-1 section (which
now cites this reproduction explicitly as the rebuttal to a "malformed
model" explanation).
2026-07-29 (later still) — /db/NMAS reproduction #12: calling-machine location doesn't matter either¶
The user set up a second machine for this investigation: "A PC" runs Gen NX
itself plus a Codex CLI instance, both on the same host/LAN; this repo and
every prior reproduction had been run from a separate dev machine ("B PC"),
reaching Gen NX only through the public relay
(moa-engineers.midasit.com). Codex on A PC built its own client on top of
this SDK and called POST /db/NMAS — and it did not crash. That result, on
its own, would have reopened everything: eleven reproductions had already
ruled out idle timeouts, modal dialogs, and model topology, but never
whether the request's network path (same-LAN-as-the-product vs. a remote
relay round trip) was the actual variable.
Before trusting it, the target node needed to be checked. Asked for the
exact call, the user reported the response was [Warning] DTO_NMAS.999999
The command is ignored because the specified node does not exist in the
model — node 999999 was never created in the open document. Every prior
reproduction (this file's and docs/vendor_repro_nmas.py's alike) creates
the target node with POST /db/NODE immediately before writing its mass,
specifically so the write reaches the actual crash-prone code path rather
than an earlier existence check. A's "success" was a clean, correct
rejection of bad input — evidence the validation layer works, not evidence
the write path is safe.
To settle it, docs/vendor_repro_nmas.py --product gen (unmodified, the
same script already used for reproduction #3 against a real production
model) was run from B PC once more, immediately after getting the Gen
MAPI-Key:
[ 0.9s] 201 POST /db/NODE 9001,9002 (0.51s)
[ 1.3s] 201 POST /db/SKEW 9001 (0.44s)
[ 1.8s] 201 POST /db/CONS 9002 (0.46s)
[ 2.2s] 200 GET /db/NMAS (대상 테이블) (0.41s) {"message": ""}
[ 2.6s] 200 GET /db/NODE (0.40s)
[ 18.0s] TIMEOUT POST /db/NMAS 9001 (15.37s)
[ 18.4s] 404 GET /mapikey/verify (릴레이만 응답)
[ 33.7s] TIMEOUT GET /db/NODE (15.35s)
Died identically — and the user immediately confirmed Gen NX on A PC had also gone down ("죽음"), from a call made on B PC. That is the decisive part: A PC and B PC were both looking at the same live Gen NX session the whole time, so a crash triggered from B PC killed the process A PC was using. Calling-machine location cannot be the variable — there was only ever one process, and it dies regardless of which machine's HTTP request reaches it, as long as the target node actually exists.
Running total after this section: 12/12 — eight on Civil NX, four on Gen
NX, zero survivals. Updated CLAUDE.md, NodalMassPayload's docstring in
db/static_loads.py, the crashes= note in live_crud_check.py, and
docs/vendor_report_ko.md's A-1 section with a fourth excluded hypothesis
(calling-machine location).
2026-07-29 (final) — /db/NMAS's actual root cause: omitting rmX/rmY/rmZ¶
A PC's Codex instance built its own client on this SDK and reported one
more data point: POST /db/NMAS with {"mX": 1.0, "mY": 1.0, "mZ": 1.0,
"rmX": 0.0, "rmY": 0.0, "rmZ": 0.0} — all six fields, not just the three
this file's every prior reproduction sent — succeeded, verified with a GET
and cleanly deleted afterward. That is a real difference in payload shape,
not a location or SDK-version effect (the previous section had already
shown A PC and B PC hit the identical Gen NX session), so it needed
isolating properly rather than accepted at face value.
First check: same session, immediately after A PC's success, from B PC.
docs/vendor_repro_nmas.py --product gen (unmodified — omits rmX/rmY/rmZ,
same as every prior reproduction) was run right away and died on the same
live session A PC had just used successfully:
[ 0.9s] 201 POST /db/NODE 9001,9002 (0.51s)
[ 1.3s] 201 POST /db/SKEW 9001 (0.44s)
[ 1.8s] 201 POST /db/CONS 9002 (0.46s)
[ 2.2s] 200 GET /db/NMAS (대상 테이블) (0.41s) {"message": ""}
[ 2.6s] 200 GET /db/NODE (0.40s)
[ 18.0s] TIMEOUT POST /db/NMAS 9001 (15.37s)
Same live process, same account, minutes apart: full fields survived,
omitted fields died. That is close to conclusive on its own, but the two
calls used different HTTP clients (A PC's Codex-built client vs. this
repo's requests-based script), so a same-tool, same-session A/B was still
worth doing before rewriting the payload contract.
Same-session A/B, one script, two nodes, Gen NX (after a restart):
--- 1) node 9101, full fields (mX,mY,mZ,rmX=0,rmY=0,rmZ=0) ---
[ 3.7s] 201 POST /db/NMAS 9101 (full fields) {"NMAS": {"9101": {...,"rmX":0,"rmY":0,"rmZ":0}}}
[ 4.1s] ...GET /mapikey/verify, GET /db/NODE - both fine, session alive
--- 2) node 9102, omitted fields (mX,mY,mZ only) ---
[ 0.5s] 200 GET /db/NODE (pre-check) - confirmed alive first
[ 15.9s] TIMEOUT POST /db/NMAS 9102 (omitted fields) - died
[ 16.3s] 404 GET /mapikey/verify (relay only)
[ 31.8s] TIMEOUT GET /db/NODE
One script, one session, one node created fine, mass written with all six fields — survives. A second node, mass written with three fields omitted — kills the exact session that had just survived the first call, seconds earlier. There is no cleaner isolation than that.
Symmetric confirmation on both products, same day, after both were restarted: a single script ran the identical two-step protocol (full fields on node N, then omitted fields on node N+1, in the same session) against Civil NX and then Gen NX in sequence:
| Product | Full fields (rmX/rmY/rmZ=0.0) |
Omitted fields |
|---|---|---|
| Civil | survived (201, GET /db/NODE fine after) |
died (15.41s timeout, then GET /db/NODE timeout) |
| Gen | survived (201, GET /db/NODE fine after) |
died (15.46s timeout, then GET /db/NODE timeout) |
The user independently confirmed both products' UI showed the crash dialog after their respective "omitted fields" call, and neither did after the "full fields" call moments earlier.
Root cause: /db/NMAS crashes when rmX/rmY/rmZ are absent from the
payload — an uninitialized-value read or missing-default bug server-side
for those three fields specifically — not a defect in nodal-mass writes in
general. Re-checked against the official article directly (not the
vendored copy, per this file's own "fetch the Zendesk article" lesson from
the 2026-07-27 section-B correction):
support.midasuser.com/hc/en-us/articles/35952994344985-Nodal-Masses's
Specifications table documents rmX/rmY/rmZ as Optional with Default
0, exactly as the vendored manual said — so every omitted-fields request
this investigation sent was fully spec-compliant, and the server crashing
on it is unambiguously a defect, not a documentation gap or a caller
mistake. The server apparently doesn't apply that documented default
safely when the fields are missing, but is fine when a caller supplies it
explicitly. This explains every prior reproduction (this file's,
docs/vendor_repro_nmas.py's, and live_crud_check.py's fixtures all
omitted these fields) without contradicting any of it — the crash was
always real, just narrower in scope than "the whole endpoint is broken"
implied.
Fix applied: NodalMass.create()/.update() in db/static_loads.py
now merge {"rmX": 0.0, "rmY": 0.0, "rmZ": 0.0} under any item that
doesn't set them, before sending — exactly the documented default, made
explicit because the server can't be trusted to apply it itself. Verified
live through the SDK (not just raw requests): a NodalMass.create() call
passing only mX/mY/mZ succeeded and the session stayed alive, and
scripts/live_crud_check.py --product gen --tier static --include-crashers
completed /db/NMAS's full create→read→update→read→delete→read round trip
cleanly (9/9 for the tier). The case is un-quarantined and confirmed=True
in live_crud_check.py as of this session.
Updated everywhere this was recorded: CLAUDE.md, NodalMassPayload's
docstring in db/static_loads.py (now describes the fix, not just the
symptom), live_crud_check.py (case un-quarantined, module docstring's
"42 of 43" corrected to all 43), and docs/vendor_report_ko.md's A-1
(rewritten to lead with the root cause and the workaround).
2026-07-29 — the 20 Civil 404s independently reproduced, PRODUCTS corrected¶
The caveat below asked for independent reproduction — different account,
different session — before touching PRODUCTS for the 20 endpoints found
404ing on the Civil v2.2 (07/28/2026) sweep. That arrived the same day: a
separately run validation sweep (docs/Codex Report/midas_nx_0.14.0_civil_*
and ..._gen_*), executed from a different machine/session against the
same day's freshly patched Civil and Gen builds, landed on the exact same
20 Civil endpoints (db_and_design_404_endpoints in its JSON summary) and
the exact same 7 Gen endpoints (CMCS, EWSF, PLCB, RCHK, SPAN,
STRPSSM, WVLD — the latter three already CIVIL_ONLY/HYPER_S_ONLY in
the SDK, so unsurprising; the rest confirm the existing accounting). Both
tools also agree the 20 Civil 404s are 404 at the /info schema level too,
not just the GET route.
That satisfies this file's own reproduction bar, so all 20 are now declared
GEN_ONLY (new constant in db/base.py, same pattern as HYPER_S_ONLY):
the 11 db/* endpoints (STOR, SWIND, SSEIS, POSP, EPST, DRLS,
SDHY, SDIS, REBB, REBR, REBW) and the 9 design-chapter endpoints
(/DESIGN/RC/KDS-41-20-2022/{MATD,REBB,REBC,REBR,REBW,TRFT,ULCT},
/DESIGN/SRC/AIK-SRC2K/MATD, /DESIGN/STEEL/KDS-41-30-2022/ULCT). Guarded
by a parametrized test, tests/db/test_civil_v22_gen_only_products.py,
mirroring test_hyper_s_products.py.
The independent sweep also surfaced three endpoints not previously in this
file's list, all looking like the same pattern but not yet independently
reproduced twice, so PRODUCTS was deliberately left unchanged for them:
/db/REBC(ch24, POST-only, no GET) — its/infoschema answers on Gen, 404s on Civil./ope/STORY_PARAMand/ope/STORY_IRR_PARAM— GET succeeds on Gen, 404s on Civil.
2026-07-29 (later same day) — live re-verification with both products open¶
With Civil NX and Gen NX both connected in the same session, re-ran every
classification claim above directly through the SDK (MidasClient(...,
strict_product=False) on both clients, so the SDK's own PRODUCTS guard
couldn't short-circuit the call — the point was to hit the real server, not
re-confirm the guard against itself):
| Family | Classification | Probed | Result |
|---|---|---|---|
GEN_ONLY (the 20 corrected above) |
civil GET fails, gen GET ok | 20/20 | 20/20 matched, byte-identical to the Civil v2.2 sweep and the independent Codex sweep |
HYPER_S_ONLY (13 implemented -M1 endpoints) |
civil GET ok, gen GET fails | 13/13 | 13/13 matched, no change since 2026-07-26 |
Both families are now confirmed a third time (GEN_ONLY) or a second time
(HYPER_S_ONLY, unchanged since first confirmed) with zero mismatches.
The 3 previously-unconfirmed candidates also confirmed this session — this is their second independent reproduction, so this file's own bar for acting is now met:
/db/REBC(ch24, POST-only):.info()answers on Gen, 404s on Civil./ope/STORY_PARAM: GET succeeds on Gen, 404s on Civil./ope/STORY_IRR_PARAM: GET succeeds on Gen, 404s on Civil.
Action taken: db.design.ColumnRebar (/db/REBC) is now PRODUCTS =
GEN_ONLY, guarded in the same test_civil_v22_gen_only_products.py test
(now 21 endpoints). docs/coverage.json updated to match.
/ope/STORY_PARAM and /ope/STORY_IRR_PARAM are documented as Gen-only in
get_story_check_parameter's and get_story_irregularity_check_parameter's
docstrings in ope.py, but not enforced — plain doc.py/ope.py/
view.py functions have no PRODUCTS gate anywhere in this SDK (they're
functions, not DbResource subclasses), so a Civil call against either
still reaches the server and 404s there rather than raising client-side.
Adding a product-gate mechanism to that whole module family would be a
bigger design change than this session's scope — flagged for a future
decision, not done here.
Final classification as of this session (all three families now
independently confirmed at least twice, zero open mismatches):
GEN_ONLY = 21 endpoints (the 20 above + /db/REBC), HYPER_S_ONLY = 13
implemented + 8 undocumented-stub -M1 endpoints (unchanged), CIVIL_ONLY
= the ch08/ch17 bridge/moving-load chapters plus the 7 endpoints corrected
2026-07-29 morning (CMCS and friends) — unchanged, not re-probed this
session since the user's request was specifically the Gen-only and
Hyper-S-only families plus the 3 unconfirmed candidates.
2026-07-29 (later still) — STORY_PARAM/STORY_IRR_PARAM against a real production Gen NX model, and a manual defect found in the process¶
The prior session's Gen NX GET check for /ope/STORY_PARAM//ope/STORY_IRR_PARAM
ran against whatever model happened to be open — good enough to confirm the
route answers, not enough to confirm it answers meaningfully. With a real
production model open (/ope/PROJECTSTATUS: 4044 nodes, 4686 elements, 11
stories, 441+494+108 load combinations across three design codes — not a
scratch model), re-ran the GET calls directly:
GET /db/STOR -> 11 real stories (B1, 1F..9F, Roof) with populated
wind/seismic eccentricity and torsional data
GET /ope/STORY_PARAM -> {"COUNTRY_CODE": "NTC2018"}
GET /ope/STORY_IRR_PARAM -> {"COUNTRY_CODE": "NTC2018",
"STORY_DRIFT_METHOD": "Drift at the Center of Mass",
"STORY_STIFFNESS_METHOD": "1 / Story Drift Ratio"}
Both endpoints answered with real, previously human-configured values (not
defaults or an empty shape), settling the "does this route actually work on
Gen, not just technically respond" question the user raised. No analysis run
was needed — both endpoints are pre-analysis configuration (country code and
calculation-method selection used by later story-drift/irregularity result
tables), not analysis output, so /doc/ANAL was never called this session.
Found in the process: a real manual defect, not just a confirmation.
StoryIrregularityCheckParameterArgument in ope.py had documented
STORY_DRIFT_METHOD/STORY_STIFFNESS_METHOD/SEISMIC_BEHAVIOR_FACTOR as
space-stripped ("Max.DriftofOuterExtremePoints", "1/StoryDriftRatio",
"3orbelow"), reasoning the manual's own worked JSON example was more
concrete evidence than the Parameters table's space-containing rendering.
The live GET above returned the space-containing form
("Drift at the Center of Mass", "1 / Story Drift Ratio") for a
real, human-set value — directly contradicting that worked example, and
matching both the Parameters table and the sibling post/story.py
STORY_DRIFT endpoint's own already-live-confirmed convention (see that
file's STORY_DRIFT_METHOD comments, chapter 21_POST_StoryTables.md).
Corrected ope.py's docstring/comment and tests/test_ope.py's example
values to the space-containing form as canonical. Not runtime-enforced
either way — TypedDicts here are documentation, not validation — but this
is exactly the "manual's worked example is the outlier, live evidence and
the Parameters table agree" pattern this project has hit before (KDS2016,
Story Drift X_DIR/X-DIR).
2026-07-29 (still later) — the mirror-image finding: 32 of 47 "Civil-only" endpoints answer on Gen too¶
Asked to also re-verify the plain CIVIL_ONLY family (excluding Hyper-S,
already reconfirmed above) with both Civil NX and Gen NX open at once.
Enumerated all 47 non-Hyper-S CIVIL_ONLY DbResource classes and probed
each with MidasClient(..., strict_product=False) on both clients (same
methodology as the GEN_ONLY/HYPER_S_ONLY re-check above — bypass the
SDK's own guard to see what the server actually does).
32 of the 47 answered on Gen NX too — not the expected 404. Checked
/info/db/... schema for all 32: every one resolves a full JSON Schema on
Gen, the same evidentiary bar this file has used everywhere else to call a
route "genuinely exists" rather than "coincidentally didn't error." Content
of the 32 GET responses:
/db/LCOM-CONC(Load Combinations - Concrete Design): 494 real, populated rows — matching this exact production model's own/ope/PROJECTSTATUScount ('Load Comb(Concrete)': 494). Unambiguous: this is a real, working, populated Gen NX feature, not an artifact.- The other 31 returned
{"message": ""}(the standard empty-table shape) — ambiguous on GET alone, but the matching/infoschema on all 31 is the same signal this file already treats as sufficient elsewhere.
The 32: IMPF, LCOM-CONC, LCOM-STLCOMP, LLAN, LLANch, LLANid,
LLANop, LLANtr, MLSP, MLSR, MVCD, MVCT, MVCTbs, MVCTch,
MVCTid, MVCTtr, MVHC, MVHL, MVHLtr, MVLD, MVLDbs, MVLDch,
MVLDeu, MVLDid, MVLDpl, MVLDtr, SINF, SLAN, SLANch, SLANop,
THGC, ULFC.
This directly contradicted db/base.py's own CIVIL_ONLY docstring,
which cited /db/LCOM-CONC and "the entire ch08/ch17 bridge/moving-load
chapters" as the canonical example of this constant's use — that example
turned out to be wrong.
The remaining 15 stayed genuinely Civil-only (404 on Gen, both GET and
by extension the route): CAMB, CJFG, CMCS, CRGR, DYFG, DYLA,
DYNF, EWSF, GCMB, GSBG, PLCB, RCHK, SPAN, STRPSSM, WVLD —
7 of these are the ones already corrected 2026-07-26 after independent Gen
404s, so no surprise there; the other 8 (bridge girder/camber-control trio
plus the railway/concurrent-group set) are newly reconfirmed, not new
corrections.
Decision, made with the user (not unilateral): given the scale (32
endpoints) and that it contradicts documented domain framing the author
themselves wrote, this was raised as an explicit question rather than
auto-applied. The user's read on it: the API clearly does respond
("분명히 통신이 되는데 only라고 쓴 것은 아닌 것 같음" — "it clearly
communicates, so writing 'only' doesn't seem right"), and whether an
engineer should actually drive bridge/moving-load features from a Gen NX
session is a separate, per-project judgment call ("목적에 따라 다르겠지만
엔지니어가 판단해서 사용해야 할 것 같음") — not something PRODUCTS
should gate. Confirmed: apply the same evidentiary bar used for
GEN_ONLY/HYPER_S_ONLY and correct all 32.
Action taken: all 32 classes' PRODUCTS = CIVIL_ONLY override removed
(back to the class default, gen+civil) across db/moving_loads.py (23),
db/analysis_control.py (5, the MVCT family), db/load_combinations.py
(2), db/dynamic_loads.py (1, THGC), db/bridge.py (1, ULFC).
docs/coverage.json updated to match (24 of the 32 needed a products
fix; 8 already said ["gen","civil"] there despite the SDK's PRODUCTS
disagreeing — the manual-derived coverage metadata had actually been right
and the SDK's own class attribute was the stale one). Five tests
(test_*_is_civil_only in test_analysis_control.py, test_dynamic_loads.py,
test_load_combinations.py ×2, test_moving_loads.py) that asserted a
ProductMismatchError on the Gen client were rewritten to assert the call
now succeeds instead. Module-level docstrings in moving_loads.py,
bridge.py, and the CIVIL_ONLY constant itself in db/base.py corrected
to stop citing the wrong example and to record the finding.
During this session's live probing, the user observed a brief popup on
the Civil NX side ("Civil에 뭔가 거부되었다는 팝업") — normal again by
the time it was reported, popup already dismissed, no session hang.
Everything run against Civil in this session was GET/.info() only (no
POST/PUT/DELETE), so the trigger isn't identified yet. The user was
re-attempting to reproduce it with a screenshot at the time of this note;
if a cause is confirmed, record it here rather than assuming GET is
unconditionally safe against this server the way this file has assumed
throughout.
2026-07-29 (final) — GET /db/CAMB can trigger the same Program Files write-permission dialog as crash recovery, confirmed A/B¶
While reproducing the CIVIL_ONLY sweep above for the user to watch, a modal
popup flashed on the Civil NX side mid-run: "...5 FCM General.mcb
액세스가 거부되었습니다" (access denied), for the product's own bundled
tutorial file at C:\Program Files\MIDAS\MIDAS CIVIL NX\MIDAS CIVIL
NX\Tutorial\5 FCM General.mcb. The user had that FCM tutorial model open at
the time. The API call itself still answered normally — this is the same
"HTTP success, modal dialog on the side" pattern this file has documented
before (see the crash-recovery _restore.mcb case and the /doc/SAVEAS
case in CLAUDE.md).
Isolated the trigger with a controlled A/B, same session, same call, one variable changed:
| Document location | GET /db/CAMB (FCM Camber Control) |
Popup |
|---|---|---|
C:\Program Files\...\Tutorial\5 FCM General.mcb |
{"message": ""} |
Yes |
Same file, moved to Downloads |
{"message": ""} |
No |
Confirmed: /db/CAMB — a plain read GET, not a write, not a crash — can
still trigger this dialog, whenever the open document sits in a
write-protected location. This generalizes the previously-known
crash-recovery-only case (_restore.mcb under Program Files, first seen
during the Gen NX hang investigation) into a broader pattern: some
GET-shaped commands apparently attempt to write an auxiliary/cache file next
to the document even to answer a read, and when that write is denied by
Windows, the dialog surfaces regardless of the command's nominal read/write
classification. Recorded as vendor report A-7's second, independently
reproduced trigger (docs/vendor_report_ko.md).
Practical implication for this SDK and its scripts: GET is not
unconditionally side-effect-free against this server the way this file has
assumed throughout — a document living under Program Files (or any other
path a standard account can't write to) is the actual risk factor, not the
HTTP method. scripts/live_readonly_sweep.py's docstring claim ("SAFE TO
RUN AGAINST AN OPEN MODEL... issues GET only") still holds for data
safety (no mutation, no discarded work) but should not be read as "will
never pop a dialog" — advise users to keep working documents off
Program Files-style paths before a sweep, the same advice already given
for A-7's crash-recovery case.
2026-07-29 (last) — full CRUD re-run on the newest Civil build, 43/43 clean including NMAS¶
With the user's FCM model already saved aside as a disposable copy, ran
scripts/live_crud_check.py --product civil --include-crashers against the
same live Civil NX session (this discards the currently-open document via
/doc/NEW, same as always — the FCM file itself was unaffected since it
was saved separately first). All 6 tiers, all 43 resources, full
create→read→update→read→delete→read round trip: 43/43 PASS, zero
failures, including /db/NMAS (create=ok read_back=ok update=ok
read_updated=ok delete=ok read_deleted=ok) — the first full Civil-side CRUD
confirmation of the omitted-rmX/rmY/rmZ fix from earlier today, not
just the Gen-side one already recorded above. Also ran a fresh
live_readonly_sweep.py --product civil --record-coverage against the FCM
model itself (before the CRUD run): 273/273 GET-capable resources answered,
zero 404s — a clean end-to-end confirmation that today's GEN_ONLY/
CIVIL_ONLY corrections match live behavior with no regressions.
Followed by scripts/live_crud_check.py --product gen --include-crashers
against the same-day Gen NX session: 37/37 PASS, zero failures,
including /db/NMAS again. The 6 missing cases versus Civil's 43 are
/db/CMCS (stage tier) and the whole moving tier (4 cases) — both
declared products=("civil",) inside the checker script itself, which
wasn't updated for today's CIVIL_ONLY→gen+civil corrections above.
Worth widening in a future session (MVCD/LLAN/MVHL/MVLD are now
confirmed to work on Gen too), but out of scope for this one.
2026-07-29 (very last) — /db/MVCD's Gen availability is per-CODE, not unconditional¶
Tried to widen scripts/live_crud_check.py's moving tier to also run on
Gen NX, since today's finding above showed the whole ch08 chapter answering
there. The checker's own discipline caught a real nuance immediately: its
/db/MVCD seed (CODE: "KOREA") came back REGRESS — a live 201 carrying
[Error] Errors detected in Moving Load Code Data.(Item:Unavailable moving
load code).
Isolated with a quick sweep of every documented CODE value against Gen:
KOREA -> [Error] ... Unavailable moving load code
CHINA -> [Error] ... Unavailable moving load code
KSCE-LSD15 -> [Error] ... Unavailable moving load code
AASHTO STANDARD -> created ok
AASHTO LRFD -> created ok
EUROCODE -> created ok
BS -> created ok
So /db/MVCD genuinely creates and round-trips on Gen NX — for the
US/Euro/UK codes. The Korea/China-specific codes 201 with an error there,
presumably a licensed-module gate scoped to the region code rather than a
route-level product restriction. This doesn't contradict the GEN_ONLY
finding above (route + /info both legitimately resolve on Gen) — it
refines it: route existence and per-value availability are different
questions, and GET/.info() alone can only answer the first.
Action: live_crud_check.py's core-tier /db/MVCD case now uses
"AASHTO STANDARD"/"AASHTO LRFD" instead of "KOREA"/"AASHTO LRFD", so
it runs unmodified and confirmed on both products (43/43 Civil, 38/38 Gen
after the change). The moving tier's other three cases (LLAN/MVHL/
MVHC/MVLD) stay Civil-only in this checker, since their fixture chain
is Korea-standard throughout (vehicle STANDARD_CODE: "KS-RB") and hasn't
been rebuilt around a Gen-available code — that fixture work is unstarted,
not evidence those routes are Civil-only (they aren't, per the GEN_ONLY
finding). PRODUCTS on the SDK classes themselves is unchanged by this —
this section is entirely about the checker script's fixture coverage, not
a source-level correction.
2026-07-29 (truly last) — /db/REBW's manual section doesn't match the live server at all¶
With a second real production Gen NX model opened (KDS/Korean-code
building, 4044 nodes, 4686 elements — the same model from this file's
STORY_PARAM/STORY_IRR_PARAM section above), re-ran the readonly sweep
(--record-coverage, 265/265 GET-capable Gen resources, zero errors) and
then spot-checked populated design-chapter data for manual drift, the same
way /db/STAG//db/TDNA were checked against the Civil FCM model earlier.
/db/REBW (ch24, "Modify Wall Rebar," 102 real rows) came back completely
unrecognizable against its own documented schema:
Documented (manual's own JSON Schema, matches this SDK's old WallRebarItem):
CREATE_SUB_WALL_ID, SUB_WALL_ID, STORY: {FROM, TO},
VERTICAL_REBAR: {NAME, DIST}, HORIZONTAL_REBAR: {NAME, DIST},
USE_END_REBAR, END_REBAR: {NAME, NUM, DIST},
BE_HORIZONTAL_REBAR: {NAME, DIST}, BOUNDARY_ELEMENT_LENGTH,
CONCRETE_FACE_TO_CENTER_OF_REBAR: {DW, DE},
USE_MODEL_THICKNESS, THICKNESS
Live GET /db/REBW:
{"ID": 0, "bUSE_MODEL_THICK": true, "THICK": 0, "DW": 0.05, "DE": 0.05,
"VER_BAR": {"NAME": "D16", "DIST": 0.2},
"HOR_BAR": {"NAME": "D13", "DIST": 0.25},
"END_BAR": {"NAME": "", "DIST": 0}, "NUM_END_BAR": 0,
"BE_HOR_BAR": {"NAME": "D10", "DIST": 0.2}, "BE_LENGTH": 0}
Every field is renamed (and DW/DE flattened out of any nesting), and
STORY doesn't exist as {FROM,TO} at all. Two checks isolated this to
/db/REBW specifically, not a systemic ch24 problem:
/db/REBB(ch24's sibling, "Modify Beam Rebar") matched its own documentation exactly —vMAIN_BAR_TOP,SHEAR_BAR,SKIN_BAR_NAME, all present as documented, real data./DESIGN/RC/KDS-41-20-2022/REBW(ch26, the KDS-specific sibling for the same physical walls, same 102 rows) also matched its own documentation exactly —VERTICAL_REBAR,HORIZONTAL_REBAR,CONCRETE_FACE_TO_CENTER_OF_REBAR: {DW,DE}, all present, long-form.
GET /info/db/REBW confirmed the abbreviated shape is the server's own
stated schema, not a GET-only quirk — it documents bUSE_MODEL_THICK,
VER_BAR, HOR_BAR, END_BAR, NUM_END_BAR, BE_HOR_BAR, BE_LENGTH,
and vSTORY_NAME: [string] (a story-name array, not a {FROM,TO} range)
as the Argument schema. Confirmed it's also the real write contract, not
just read: picked one existing wall (id 101), backed up its exact current
value, sent a PUT using the info-confirmed field names changing
VER_BAR.DIST from 0.2 to 0.99, verified the change landed on a fresh
GET, then restored the original value and verified the restore matched
byte-for-byte. Full round trip, real data, real Gen NX session.
Conclusion: docs/manual/24_DB_Design.md's /db/REBW section (and by
inheritance this SDK's old WallRebarPayload/WallRebarItem) documents a
schema the live server doesn't implement. A user following the manual's
worked example would have every field silently ignored or rejected — this
isn't a cosmetic drift, it's the whole payload contract. /db/REBB and the
ch26 KDS sibling being correct rules out "the whole rebar family is
misdocumented" — this looks like a defect isolated to /db/REBW's specific
manual section.
Checked against the official source directly — not just the vendored
copy. Per this file's own standing rule (never cite the vendored manual
repo as "the documentation" in anything sent externally), searched
MIDASIT's Zendesk Help Center API directly: the dedicated article for this
endpoint is
59359110968345 — "Modify Wall Rebar Data"
(distinct from
59236271208729,
the correct ch26 KDS-specific article). It documents the same long-form
names as the vendored copy (VERTICAL_REBAR, HORIZONTAL_REBAR,
CONCRETE_FACE_TO_CENTER_OF_REBAR, STORY: {FROM,TO}, ...). This rules
out a vendored-repo transcription error — the official MIDASIT
documentation itself doesn't match its own server's implementation. Direct
HTML fetches of Zendesk pages 403 for this tool; the JSON Help Center API
(/api/v2/help_center/en-us/articles/<id>.json) and article search
(/api/v2/help_center/articles/search.json?query=...) work and are the
way to reach a specific official article when a chapter's combined
reference article is too large to fetch in full.
Fix applied: WallRebarItem/WallRebarPayload in db/design.py
rewritten to the server-confirmed shape (ID, bUSE_MODEL_THICK, THICK,
DW, DE, VER_BAR, HOR_BAR, END_BAR, NUM_END_BAR, BE_HOR_BAR,
BE_LENGTH, vSTORY_NAME), with a docstring explaining the manual
mismatch and citing this section. The now-unused StoryRange/
WallEndRebarSpec/ConcreteFaceToCenterOfRebar helper TypedDicts were
removed (ch26's RcWallStoryRange/RcWallEndRebarSpec/
RcWallConcreteFaceToCenterOfRebar are separate, correct, untouched).
tests/db/test_design_setup.py's WallRebar create test updated to the
corrected field names. This is a real correctness fix — the old TypedDict
would have led a caller straight into the exact defect this session found.
Worth reporting to MIDASIT: see docs/vendor_report_ko.md.
2026-07-29 (later same day) — the 8 Hyper-S -M1 stubs, implemented from /info/db/...¶
Per the author's decision on the v1.0.0 gate's item (a) ("1번으로 해서 진행" —
go with option 1), pulled full GET /info/db/... JSON Schema bodies for all
8 previously-unimplemented Hyper-S stub endpoints against a live Civil NX
2026 (v2.2, build 06/18/2026) session, using a direct MidasClient(...,
strict_product=False) probe (not scripts/live_readonly_sweep.py this
time — the session disconnected from the relay partway through, before the
script itself could run; the direct probe's results were already captured).
5 of 8 resolved a full schema:
| Endpoint | GET | /info |
|---|---|---|
/db/STYP-M1 |
real populated row (STYPE: "3D", GRAV: 9.806, ...) |
full schema |
/db/MATL-M1 |
real populated row (MATL_NAME: "C24", MATL_TYPE: "CONC", PARAM[0].P_TYPE: 0) |
full schema |
/db/IMFM-M1 |
{"message": ""} (empty table) |
full schema |
/db/EPMT-M1 |
{"message": ""} (empty table) |
full schema |
/db/IEHG-BEAM-M1 |
{"message": ""} (empty table) |
full schema |
/db/IEHG-TRUSS-M1 |
{"message": ""} (empty table) |
404 |
/db/IEHG-GL-M1 |
{"message": ""} (empty table) |
404 |
/db/IEHG-PSS-M1 |
{"message": ""} (empty table) |
404 |
The 3 IEHG-{TRUSS,GL,PSS}-M1 endpoints answer GET (empty table on this
model) but their own /info/db/... route 404s — no schema route exists for
them specifically, unlike their sibling IEHG-BEAM-M1.
Two of the five resolved schemas turned out to have a genuinely different wire shape from their non-Hyper-S sibling, not just a product gate on an identical schema:
/db/MATL-M1'sPARAM[].P_TYPEis 0-indexed (Standard:0, Isotropic:1, Orthotropic:2) and nests user-defined fields under aUSER_DEFINEDsub-object, vs. non-Hyper-S/db/MATL's 1-indexedP_TYPEwith flat fields (ELAST,POISN, ... directly on the param object)./db/IMFM-M1nests fields underCONCRETE/STEELsub-objects with different names entirely (UN_CONC_NAME,CONF_CONC_NAME) vs. non-Hyper-S/db/IMFM's flatCONC_NAME/CONFINED_CONC_NAME.
/db/EPMT-M1's MODEL_TYPE is also an int (Tresca:0, VonMises:1,
MohrCoulomb:2, DruckerPrager:3, Masonry:4, ConcreteDamage:5) vs.
non-Hyper-S /db/EPMT's string codes ("TR"/"VM"/"MC"/...), and adds
two sub-objects (DRUCKER, MASONRY, CONCDMG) the non-Hyper-S variant's
chapter never documented at all.
Fix applied: implemented all 8 as proper DbResource subclasses —
StructureTypeHyperS (db/project.py), MaterialHyperS /
InelasticFiberMaterialLinkHyperS / PlasticMaterialHyperS
(db/properties/material.py), InelasticHingePropertyHyperS{Beam,Truss,
GeneralLink,Pss} (db/properties/hinge.py). Each payload TypedDict is
documented in its own docstring as /info-derived rather than
manual-transcribed. The 3 IEHG-* classes without their own /info route
carry an explicit ⚠️ docstring caveat that their single-field shape
(INEL_PROP_NAME) is assumed by sibling analogy to IEHG-BEAM-M1, not
independently confirmed — if that assumption is ever wrong, it'll surface
as a "Wrong Field" response on a real write, per this file's own
"Wrong Field usually means a bad value, not a bad field name" note (which
doesn't apply here since these would be genuinely wrong field names, an
exception worth remembering if it comes up).
docs/coverage.json updated: all 8 endpoints marked "status":
"implemented" with a live_verified entry citing this direct-probe method
(distinct from the sweep script's own live_readonly_sweep.py method
string, since the sweep script itself didn't get to run). Coverage is now
398/398 (100%). tests/db/test_hyper_s_products.py's family-size assertion
updated 13→21. This resolves the v1.0.0 gate's item (a) — see PLAN.md.
Two corrections caught by /code-review on 2026-07-30, after v1.0.0 shipped:
InelasticHingePropertyHyperSPss's docstring claimed "PSS" isn't stated in any available source — false. The manual repo'sINDEX.mdtitles the endpoint "Assign Inelastic Hinges — Point Spring Support (Hyper-S)", and04_DB_Properties.md's own chapter TOC calls it "... Point Spring (Hyper-S)" (a minor internal inconsistency in the manual — "Support" present in one title, absent in the other — but "Point Spring" either way). What's actually missing is a Specifications table, not the name. Fixed the docstring and itsNAMEfield (now "... Point Spring, Hyper-S)"). This is why the day's own rule matters: verify a claim against the actual source before asserting it, even a claim about what a source doesn't say.docs/coverage.json'slive_verified.methodstring was identical for all 8 Hyper-S stubs, which overclaimed schema confirmation forIEHG-{TRUSS,GL,PSS}-M1— their/inforoute 404s, so only GET was actually confirmed for them, unlike the other 5. Reworded theirmethodfield to say so explicitly, so a future reader of the coverage ledger (orscripts/check_manual_drift.py) doesn't skip re-verifying these three on the strength of a probe that never actually ran against their schema.
2026-07-30 — second independent live re-check of the 8 Hyper-S stubs¶
With a fresh Civil NX session (same build, v2.2 06/18/2026), re-ran
scripts/live_readonly_sweep.py for real this time (2026-07-29's original
recording used a direct probe, since the session disconnected — see above —
before the script itself could run). All 8 answered ok: STYP-M1,
MATL-M1, IMFM-M1, EPMT-M1, IEHG-BEAM-M1, IEHG-TRUSS-M1,
IEHG-GL-M1, IEHG-PSS-M1.
Also re-probed /info/db/... directly for all 8 to check the schema/no-schema
split still holds: it does, exactly. The 5 with a schema
(STYP-M1/MATL-M1/IMFM-M1/EPMT-M1/IEHG-BEAM-M1) returned the
identical field set as 2026-07-29 — a second independent confirmation, not
just a repeat of the same probe. The 3 without one (IEHG-TRUSS-M1,
IEHG-GL-M1, IEHG-PSS-M1) still 404 on /info — their INEL_PROP_NAME
shape remains an assumption by sibling analogy to IEHG-BEAM-M1, not
independently confirmed, and there is still no indication MIDASIT intends to
add an /info route for them.
docs/coverage.json's live_verified entries for all 8 updated to cite
scripts/live_readonly_sweep.py (the intended method, now that it actually
ran) with today's date, keeping the same schema-confirmed/unconfirmed
distinction from the code-review fix above.
2026-07-30 — a real AASHTO arch-bridge model, and rebuilding the moving-tier fixture¶
With a different real production Civil NX model open (an AASHTO-coded arch
bridge), ran a full scripts/live_readonly_sweep.py (281/281 ok, zero
404s — reconfirms every PRODUCTS classification holds on a third,
structurally distinct real model) and spot-checked the genuinely
CIVIL_ONLY bridge-chapter endpoints (CMCS, EWSF, PLCB, RCHK,
SPAN, STRPSSM, WVLD, CAMB, CJFG, CRGR, DYFG, DYLA, DYNF,
GCMB, GSBG) — all answer cleanly with 0 rows, meaning this arch bridge
simply doesn't use girder-camber/composite-girder features, not an error.
The moving-load chapter had real, meaningful AASHTO LRFD data: /db/MVCD
(CODE: "AASHTO LRFD"), /db/MVHL (the actual HL-93 design truck/tandem,
HL-93TRK/HL-93TDM, STANDARD_CODE: "AASHTO-LRFD"), /db/LLAN (2 real
lanes with populated LANE_ITEMS), and /db/MVLD (a load case combining
both HL-93 vehicles with the AASHTO SCALE_FACTORS combination rule).
Cross-checked field-for-field against VehiclePayload, TrafficLineLanePayload,
and MovingLoadCasePayload in db/moving_loads.py: zero drift — every
field name, nesting, and type matched. This also independently reconfirmed
(against genuine production data, not a synthetic test) the existing
LineLaneItem docstring warning that CENT_F must be nonzero when
MVCD.CODE="AASHTO LRFD" (CENT_F: 0.5 in the real data).
Follow-on: rebuilt scripts/live_crud_check.py's moving tier fixture
around this AASHTO LRFD/HL-93 shape, replacing the Korea-standard
(MVCD "KOREA", STANDARD_CODE "KS-RB") fixture that had kept the tier
Civil-only in the checker since 2026-07-29 (Korea/China/KSCE-LSD15 codes
hit a licensed-module gate on Gen; AASHTO does not). Ran it live against
this same Civil session — the user explicitly authorized this knowing it
calls /doc/NEW and discards the open arch-bridge model ("자유롭게 해") —
and all 4 resources (LLAN/MVHL/MVHC/MVLD) passed a full
create→read→update→read→delete→read round trip on the first run. Still
products=civil in the checker for now: the fixture is now
code-portable and the route-level evidence says it should also pass on
Gen, but that is an expectation, not something watched pass live yet —
widen products/flip to Gen-confirmed only once someone actually runs
--tier moving --product gen.
2026-07-30 (same day) — a real Eurocode PSC bridge model finds four schema gaps¶
Same session, model swapped to a different real production Civil NX model:
a PSC (prestressed concrete) bridge built with Eurocode, evidently by the
Free Cantilever Method (4 construction stages, ACT_ELEM/ACT_BNGR/
ACT_LOAD populated per stage, 24 real tendon profiles across groups
A1-C4). Full live_readonly_sweep.py again 281/281 ok. The genuinely
CIVIL_ONLY bridge-chapter endpoints all answered with 0 rows (this bridge
type doesn't use girder-camber features), same as the arch-bridge model
above — reconfirms PRODUCTS holds on a third, structurally distinct real
model.
Cross-checked real tendon/prestress/creep-shrinkage/moving-load data against
their TypedDicts (db/temperature_prestress.py, db/properties/material.py,
db/moving_loads.py) and found four confirmed gaps — smaller in scope than
/db/REBW but the same species of defect (manual under-documents, /info
and real data reveal more):
/db/TDNT(TendonPropertyPayload) was missingbRELAX("Relaxation coefficient - Check Box", boolean) — present in this model's real tendon data and in/info/db/TDNT, absent from both the manual's Specifications table and the TypedDict./db/TDPL(TendonPrestressItem) was missingGROUP_NAME("Group Name", string) — same story: real data has"GROUP_NAME": "PS1"on every item,/info/db/TDPLconfirms it, the manual's table doesn't mention it./db/TDMThas a third code branch,CODE="EUROPEAN", entirely undocumented. This model's concrete (C40/50) uses it with two dedicated fields,TCODE(int) andbSILICA(bool), neither in the manual's table nor the TypedDict (which only covered CEB-FIP and ACI).GET /info/db/TDMTreveals this endpoint's real schema is far larger still — roughly 70 fields spanning many more codes (JSCE, GB, JTG, and others) — only the EUROPEAN branch's two extra fields were added; the rest is noted as a known gap, not fixed, in the TypedDict's own docstring./db/MVHL(VehiclePayload) has 11 more country-specific sub-objects the manual never mentions at all.GET /info/db/MVHLlistsVEH_FR,VEH_CN,VEH_IN,VEH_CA,VEH_BS,VEH_EUROCODE,VEH_RU,VEH_KSCE_LSD15,VEH_AU,VEH_PL,VEH_ZAalongside the documentedVEH_DEFAULT, plus a second load-items arrayLOAD_ITEMS2. This model's real Eurocode "Load Model 1" vehicle (MVLD_CODE: 11) usesVEH_EUROCODEinstead ofVEH_DEFAULTand — more surprising — omitsSTANDARD_CODEentirely, even though the manual's own Specifications table marks it "Required".VEH_EUROCODEitself has ~50 fields (/infoschema) covering multiple Eurocode load-model sub-types (LM1/LM2/LM3, rail HSLM-A/B, permit loads); the flat scalar ones are now typed asVehicleEurocodeParams, with the three deeply nested, load-model-specific arrays (LOADCASES/VEHICLES/PERMIT_LOAD) left untyped, same treatment asSECT_I's precedent. The other 10 country-specific sub-objects are confirmed to exist via/infobut not individually typed — a real, larger backlog item if someone needs France/China/India/Canada/etc. moving-load support beyond what already works throughVEH_DEFAULT.
All four fixed in src/, plus a new test (test_vehicles_create_sends_veh_eurocode_without_standard_code)
covering the VEH_EUROCODE-without-STANDARD_CODE shape. 681 tests passing,
ruff clean. This is a genuine src/ correctness improvement (four
previously-unrepresentable real payload shapes now have accurate
TypedDicts) — adds to the pending version-bump case.
2026-07-30 (same day) — a real Eurocode railway bridge: confirms VehicleEurocodeParams, finds a CENT_F scope surprise¶
Model swapped again, same Civil session: a Eurocode railway bridge. Full
sweep 281/281 ok again; the CIVIL_ONLY railway-specific
RailwayDynamicFactor (/db/DYFG) has real, meaningful computed data
(INPUT_TYPE: 0 = Auto, DYN_FACTOR: 1.148... computed) that matches
RailwayDynamicFactorPayload exactly — no drift.
/db/MVHL has three real Eurocode rail load models — "Load Model 71",
"Load Model SW/0", "Load Model SW/2" (SUB_TYPE: 23, distinct from Load
Model 1's SUB_TYPE: 19 seen on the PSC bridge above) — using W1, DD1,
D1, W2, DD2, D2, V_LOAD_FACTOR, LONGI_DIST, ECCEN_VERT_LOAD.
Every one of these fields was already covered by VehicleEurocodeParams,
added minutes earlier from the PSC bridge's road-vehicle example — a second,
independent real-data confirmation of that fix, this time for the rail
load-model sub-type rather than the road one.
Also surfaced a genuine scope surprise in an existing, previously-trusted
note: LineLaneItem.CENT_F (/db/LLAN's LANE_ITEMS[].CENT_F) was
documented "AASHTO LRFD only" after the 2026-07-29 finding that omitting it
under MVCD.CODE="AASHTO LRFD" gets rejected server-side. This Eurocode
railway bridge's own LANE_ITEMS carry the identical populated value
(CENT_F: 0.5) — same value as the AASHTO arch-bridge model. Left
unresolved rather than "corrected": it isn't yet clear whether CENT_F is
genuinely active under Eurocode too, or just echoed back inertly regardless
of code (as FACT/WIDTH/ECCEN_VERT_LOAD have been seen to be on codes
that don't use them). LineLaneItem's docstring now flags this as an open
question rather than asserting either way — a candidate for a future
same-value-different-code write test if it matters to a caller.
2026-07-30 (same day) — a cable-stayed/suspension bridge: one more gap, STAG's NO¶
Model swapped again to a cable-stayed bridge (this turned out to be the same
FCM model from earlier in the session — recognizable by its ELNK "Stay"
records at nodes 27/78 and 129/180, LINK: "GEN", and a GRUP "Sag" node
group covering exactly those same two i-nodes for cable sag adjustment).
Full sweep 281/281 ok again.
Cross-checked RigidLinkItem (/db/RIGD, 49 real rows), ConstraintItem
(/db/CONS, 10 real rows), and ElasticLinkPayload (/db/ELNK) against
real data — all matched exactly, no drift.
ConstructionStagePayload (/db/STAG, 8 real stages including cable-sag-
specific ones that activate/deactivate the "Stay"/"Pin Connection" boundary
groups mid-sequence) was missing NO ("Construction Stage No.",
integer) — present in every real stage record and in /info/db/STAG, not
in the manual's Specifications table. Fixed. DACT_BNGR/DACT_ELEM's
REDIST (element force redistribution %) were already correctly typed and
matched the real data exactly.
Running tally for 2026-07-30's cross-model spot-checking (four real
production Civil NX models: arch bridge, PSC bridge, railway bridge,
cable-stayed bridge): 6 schema gaps found and fixed (TDNT.bRELAX,
TDPL.GROUP_NAME, TDMT's EUROPEAN branch, MVHL's VEH_EUROCODE +
10 other undocumented country sub-objects, STAG.NO), one open question
left unresolved (CENT_F's true code scope), and one existing fix
(VehicleEurocodeParams) independently reconfirmed twice more against
different real Eurocode load-model sub-types. Zero drift found everywhere
else checked across all four models.
2026-07-30 (later) — first Gen NX pass of the day: a small solid-element cantilever model, STLD.NO gap¶
Switched to a fresh Gen NX session (v2.1, build 07/28/2026) with a small
real model open — not a bridge this time: a 5m x 0.4m x 0.2m concrete
block cantilever modeled as 10 SOLID (hexahedral) elements, fixed at one
end (CONS all-6-DOF on the 4 end nodes), one user-named load case
(STLD "CASE1", TYPE: "USER"). Small model, but real (not /doc/NEW
scratch), and a different element family (SOLID) than anything spot-
checked so far this week (all bridge frame/beam models).
Full live_readonly_sweep.py re-run: 265/265 ok, zero newly-recorded
(all previously verified) — no regressions.
Cross-checked MaterialPayload/MaterialParam (/db/MATL, P_TYPE=2
isotropic concrete), ElementPayload (/db/ELEM, TYPE: "SOLID"),
ConstraintItem (/db/CONS), and UnitPayload (/db/UNIT) against real
data — all matched exactly.
StaticLoadCasePayload (/db/STLD) was missing NO ("Ordering Index
in GUI") — present in the real record ({"NO": 1, "NAME": "CASE1", "TYPE":
"USER", "DESC": ""}) and documented in the manual's own Specifications
table (06_DB_Static_Loads.md, marked "Read Only"), just never
transcribed into the TypedDict. Same class of gap as STAG.NO two
sections up — a read-only GUI-ordering field the manual documents but this
SDK hadn't typed yet. Fixed. TYPE: "USER" ("User Defined Load") is a
real documented code, not a mystery value — first row of the Load Type
table.
2026-07-30 (later still) — 🎉 POST /db/NMAS no longer crashes on Civil NX v2.2, build 07/29/2026¶
User reported MIDASIT shipped a patch and asked to verify. Fresh Civil NX session, v2.2 build 07/29/2026 — one day newer than the build used for reproductions #10-12 (v2.2 build 07/28/2026, see "final" root-cause section above). Empty document (0 nodes), so no data at risk.
Reproduced the exact historical trigger on a raw client.request() call
(bypassing NodalMass's own rmX/rmY/rmZ auto-fill workaround, which
would have masked the question): created node 1, POST /db/NMAS with only
mX/mY/mZ set — the omitted-rotational-fields shape that killed the
session in all 15+ prior reproductions across both products. This time:
201 in 0.5s, session stayed alive. Repeated on a second node (mX/mY/
mZ again omitted-rm) since the original bug specifically needed a second
call on a different node to trigger — same result, 0.1s, alive.
verify_connection() before/after both showed "connected", and a
follow-up GET /db/NMAS showed both records with rmX/rmY/rmZ now
correctly defaulted to 0 server-side — meaning MIDASIT's fix isn't just
"stopped crashing", it's now applying the documented default like it
always should have. Cleaned up both test nodes/masses afterward; document
back to empty.
This looks like a genuine server-side vendor fix, not a fluke — same
account, same reproduction method that was 15/15 reliable before, now 2/2
clean on the newer build. Still only one account/session, so treat as
strong evidence rather than final confirmation (see the Caveat below); the
right trigger to fully retire NodalMass's auto-fill workaround is
independent reproduction of the fix (different account/session, and a
matching Gen NX patch — this was Civil NX only, Gen NX's own last-known
build (v2.1, 07/28/2026) was not re-tested here and NMAS was never
observed to be product-specific anyway). Not removing the workaround
yet — it's harmless when the field is now defaulted correctly either way
(explicit 0.0 in, explicit 0 out), and removing it prematurely would
regress hard the moment someone hits this from an unpatched build.
2026-07-30 (later) — closing the "never GET-swept" gap: /ope, /view, /post plain-function endpoints¶
Coverage stood at 304/398 live-verified with 94 endpoints unaccounted for.
Checked why: scripts/live_readonly_sweep.py only discovers DbResource
subclasses (_all_resources() walks the class hierarchy), so the whole
/doc/*, /ope/*, /view/*, /post/*, /DESIGN/* families — all plain
functions per this SDK's own layout convention — were never reachable by
the sweep regardless of whether they actually work, not because they're
untested-and-risky. Some of them already had been tested live in earlier
sessions (STORY_PARAM/STORY_IRR_PARAM on 2026-07-29) without ever
getting a coverage.json entry, since nothing writes to that file except
the sweep script's --record-coverage flag.
With both Civil NX (v2.2, 07/29/2026) and Gen NX (v2.1, 07/28/2026) connected, empty documents on both (0 nodes), manually probed the GET-shaped subset of these plain functions directly (not through the sweep tool, which doesn't know about them):
| Function | Endpoint | Civil | Gen |
|---|---|---|---|
ope.get_project_status |
/ope/PROJECTSTATUS |
ok | ok |
ope.get_section_properties |
/ope/SECTPROP |
ok (empty) | ok (empty) |
ope.get_story_check_parameter |
/ope/STORY_PARAM |
404 | ok |
ope.get_story_irregularity_check_parameter |
/ope/STORY_IRR_PARAM |
404 | ok |
view.get_selection |
/view/SELECT |
ok | ok |
post.pre_process.get_material_table |
/post/TABLE (pre-process) |
ok (empty) | ok (empty) |
STORY_PARAM/STORY_IRR_PARAM's Civil 404 is now confirmed a third
time (previously: the 2026-07-29 sweep, then the same-day live re-check
with both products open) — solid evidence for the Gen-only restriction
that this SDK documents in the functions' own docstrings but doesn't
(and, being plain functions, can't cleanly) enforce client-side. All 6
recorded into coverage.json's live_verified field; 310/398 total now.
post.pre_process.get_material_table was one spot-check standing in for
all 10 TABLE_TYPE values in that module (they share the same
post.base.get_table()/unwrap_table() plumbing) — the other 9 weren't
individually re-probed.
Left deliberately untouched this pass — all mutate the model, the document itself, or need a completed analysis first, so they get their own explicit go-ahead rather than folding into this GET-only sweep:
/doc/*lifecycle (OPEN/CLOSE/SAVE/SAVEAS/IMPORT/EXPORT/STAGAS) — touches real files on the machine running NX; this file's own history has a confirmedProgram Files-path modal-dialog trap for even GET-shaped calls, so a write-shaped one deserves more care, not less./ope/*actions (DIVIDEELEM,USLC,LINEBMLD,AUTOMESH,SSPS,EDMP,STOR,STORPROP,MEMB,GUSTFACTOR,LCOM-*,GSBG) — real model mutations (mesh, load combos, story calc); both open documents are empty right now so there's nothing to safely exercise them against yet anyway./view/*actions (CAPTURE,PRECAPTURE,ANGLE,ACTIVE,DISPLAY,RESULTGRAPHIC) —CAPTURE/PRECAPTUREwrite image files to the NX machine (same file-path risk class as/doc/*); the rest are harmless view-state toggles but weren't in this pass's scope./post/TABLE(Analysis Result / Analysis Story categories) and/DESIGN/*/*-ANALchapters — need a completed/doc/ANALrun first, and*-ANALcalls have a confirmed history of hanging Gen NX (see theCC-ANAL/BC-ANALsections above) — short-timeout-and-poll discipline applies, not a plain probe.
2026-07-30 (later still) — a synthetic frame model, and a new crash candidate: POST /ope/EDMP¶
User authorized going further into the remaining /ope/*//view/*/analysis
categories and building whatever model was needed ("모델 필요하면 직접
만들어서 진행해"). Built a synthetic 2-bay, 2-story steel frame on the same
empty Gen NX session (9 frame nodes + 4 unconnected quad nodes for a later
AUTOMESH attempt, 10 BEAM elements, 1 isotropic steel material, 1
DBUSER solid-rectangle section, base constraints, one "DL" load case,
two /db/STOR records) — all standard, previously-proven /db/* writes.
Ran a batch of the remaining mutating /ope/* actions against it:
| Action | Endpoint | Result |
|---|---|---|
assign_members |
/ope/MEMB |
ok |
get_story_properties |
/ope/STORPROP |
404 (separate open question, see below) |
calculate_story |
/ope/STOR |
ok — recalculated the two /db/STOR records' geometry-derived fields (STORY_LEVEL, WIND_FLOOR_WIDTH_Y, WIND_CENTER_Y, ...) from actual model geometry, overwriting the placeholder values seeded above; expected behavior for an auto-calculate action, not a bug |
divide_elements |
/ope/DIVIDEELEM |
ok — split element 7 into two elements at a new midpoint node |
calculate_gust_factor |
/ope/GUSTFACTOR |
ok |
change_property (TYPE="NSM", AUTO=true) |
/ope/EDMP |
🛑 30s read timeout, then Gen NX crashed |
create_line_beam_load |
/ope/LINEBMLD |
timed out — but this ran after EDMP already broke the session, so this failure doesn't mean anything about LINEBMLD itself; needs re-testing post-recovery |
POST /ope/EDMP is a new crash candidate, confirmed once: the call
itself read-timed-out (30s), a follow-up plain GET /db/NODE on the same
session also timed out, and verify_connection() still reported
"connected" throughout — the exact "modal dialog blocks every /db/*
call while /mapikey/verify keeps answering" pattern this file already
documents for other crashes. The user confirmed Gen NX showed the familiar
"[Error] Failed to disconnect the work session..." license dialog (same
one seen in the /doc/NEW and pre-fix NMAS crashes) — a full crash, not
just a slow call.
Not yet root-caused. Only one payload variant was tried
({"NODE_ELEMS": {"KEYS": [1,2,3]}, "TYPE": "NSM", "AUTO": true, "CODE":
"Korean Standard", "H_VS": 0.5}, targeting 3 BEAM elements). Unlike the
NMAS saga, this is a single reproduction with no A/B isolation yet —
could be EDMP itself, the AUTO=true auto-calculate path specifically,
the CODE value, or something about the target elements. Flagged here
rather than pursued immediately; whether this gets the full NMAS-style
isolation treatment or just a documented-and-deprioritized flag (like the
*-ANAL hangs) is the user's call once Gen NX is back up.
/ope/STORPROP (get_story_properties) 404'd on its first-ever live
call, independent of the EDMP crash (it ran two calls earlier in the same
batch, before anything broke). Open question, not yet investigated:
possibly needs calculate_story//db/STOR data in a different state,
possibly a genuine route issue, possibly a body-shape problem surfacing as
404 instead of a schema error (seen before in this project — worth
comparing against the manual's worked example rather than assuming the
docstring's FORMAT/PLACE guess is the actual cause).
2026-07-30 (later still) — after recovery: LINEBMLD/AUTOMESH/LCOM-* sorted out, then a second crash on POST /ope/USLC¶
Rebuilt the same synthetic frame (Gen NX's crash-recovery "New Project"
step discards the document, as expected) and continued through the
remaining /ope/* actions.
/ope/LINEBMLD (TARGET.METHOD) sorted out: METHOD=0 (NODE-defined
load line) works exactly per the manual's own worked examples — confirmed
live with a UNILOAD on nodes [7,8], echoed back correctly
(D/P padded to 4 elements: [0.25, 0.85, 0, 0] / [-3, -3, 0, 0]).
METHOD=1 (selected elements) still fails identically a 3rd time
(TYPE=UNILOAD and TYPE=CONLOAD, both with a real, existing element in
TARGET.ELEM) — 3/3 "Wrong Field", 0/1 success. Documented in
ope.py's LineLoadTarget docstring as unconfirmed/likely broken; not
pursued further (no untried field found that changes the outcome).
/ope/AUTOMESH needs boundary elements, and they must be frame-
capable (BEAM), not TRUSS: the first attempt used METHOD="Nodes"
with 4 isolated, unconnected corner nodes and got a generic
"MIDAS GEN NX second query is wrong" — not a documented error string,
and not obviously about a missing field. Switching to METHOD="Line
Elements" with 4 boundary elements ringing the quad (plus adding the
THICKNESS the worked example includes but the Parameters table marks
merely "Optional") got further but still failed
("[Error] Invalid element list!") when those 4 boundary elements were
TRUSS. Rebuilding the same 4 elements as BEAM succeeded immediately,
generating 4 real PLATE elements. Reads as a genuine element-type
restriction (a TRUSS — no bending stiffness — plausibly can't bound a
meshed plate region) rather than a schema documentation gap; not written
up as a defect, just noted here since the manual doesn't say which line-
element types qualify.
/ope/LCOM-CONC, /ope/LCOM-STEEL, /ope/LCOM-SRC all worked with a
minimal payload ({"OPTION": "ADD", "DGNCODE": "<per-endpoint code
string>"} — every other field really is optional as documented), each
generating 2 real combinations (STRENGTH/SERVICE) from the model's
single "DL" load case. /ope/LCOM-GEN (CODE_SELECTION="CONCRETE")
failed with "Wrong Field" on a payload built from the TypedDict's own
documented-required fields for that body
(RS_SCALE_FACTOR:[], ORTHO_EFFECT:{OPT_USE:false},
ADDITIONAL_LOAD:{SPECIAL_LOAD:{OPT_USE:false},
VERTICAL_LOAD:{OPT_USE:false}}, CS_ANALYSIS:false,
PRESTRESS_LOSS:false) — not yet investigated further, lower priority
since LCOM-CONC already covers the same design body successfully via
its own dedicated endpoint.
🛑 POST /ope/USLC crashed Gen NX — a second, independent crash
candidate this session. Payload: {"POSITION": "CONC", "LCOM_LIST":
[{"TYPE": "CONC", "NAME": "cLCB1"}, {"TYPE": "CONC", "NAME": "cLCB2"}]},
referencing two real combinations that LCOM-CONC had just generated
successfully moments earlier in the same session. Same signature as the
EDMP crash: the call itself read-timed-out (25s), a follow-up plain
GET /db/NODE also timed out, verify_connection() still reported
"connected" throughout, and the user confirmed Gen NX itself was dead
("죽음") requiring the same New-Project-then-restart recovery.
Two crash candidates from one session's /ope/* sweep (EDMP, USLC,
2 of 7 actions attempted) changes the risk calculus — this isn't a
single fluke the way early NMAS reproductions looked, it's a live
~30% hit rate on this endpoint family so far. Deliberately not
continuing to blindly try the remaining untested actions (SSPS, GSBG)
without the user's explicit go-ahead each time; asked the user directly
whether to keep pushing through /ope/* (accepting the crash risk) or
switch to safer categories (/view/*, then analysis + result tables) and
leave the rest of /ope/* flagged-but-untested, the same way *-ANAL is
handled elsewhere in this file.
/ope/STORPROP (get_story_properties) 404'd on its first-ever live
call, independent of the EDMP crash (it ran two calls earlier in the same
batch, before anything broke). Open question, not yet investigated:
possibly needs calculate_story//db/STOR data in a different state,
possibly a genuine route issue, possibly a body-shape problem surfacing as
404 instead of a schema error (seen before in this project — worth
comparing against the manual's worked example rather than assuming the
docstring's FORMAT/PLACE guess is the actual cause).
2026-07-30 (later still) — user accepted the crash risk; SSPS, ANAL, result/story tables, and both design-check *-ANAL families all pass¶
User explicitly chose to keep going despite the EDMP/USLC crashes
("계속 진행 (크래시 감수하고)"). Rebuilt the frame a third time (same
9-node/10-BEAM shape, no stories or quad nodes this round) and continued.
/ope/SSPS worked cleanly, no crash: CONVERT_TO=POINT_SPRING,
ELEMENT.TYPE=FRAME on two real BEAM elements, matching the manual's
own worked example almost exactly — generated real /db/NSPR point-spring
records with computed SDR/Cr/EFFAREA/DK fields.
/view/ANGLE, /view/ACTIVE, /view/DISPLAY all worked, no crash —
cosmetic view-state actions, as expected.
/doc/ANAL timed out (30s) — but this was a save-confirmation dialog,
not a crash. The call itself read-timed-out and the follow-up GET
/db/NODE also timed out, looking identical to the EDMP/USLC crash
signature — but the user checked Gen NX and found a plain "저장해달래"
(save-changes) prompt, not the "Failed to disconnect" crash dialog. This
is CLAUDE.md's already-documented "any confirmation dialog blocks the
whole session until dismissed" behavior, reproduced for /doc/ANAL
specifically for the first time in this file, and importantly: not
every session-blocking dialog is a crash — the two need to be told
apart by asking the user what's actually on screen, not assumed from the
timeout pattern alone. After the user saved, the session unblocked
immediately (model intact, 9 nodes) and a retried /doc/ANAL succeeded
in 5s.
/post/TABLE (Analysis Result + Analysis Story categories) both
confirmed working post-analysis: get_reaction_table and
get_beam_force_table (result_1.py) returned real per-node/per-element
rows (all zero, since the model's one "DL" load case never had an
actual load value assigned — expected, not a defect); get_story_drift_table
(story.py) returned a valid empty response (no lateral/seismic load to
report on). One spot-check per category, not all ~25/~16 table types.
/view/RESULTGRAPHIC worked (CURRENT_MODE="reactionforces/moments")
now that real analysis results existed.
Both design-check *-ANAL families tested — neither hung. User
explicitly accepted the extra risk here too (this family's docstring
already warns of a documented Gen NX hang requiring a forced process
kill, a step beyond anything recovered from today via the graceful
New-Project dialog flow). perform_steel_code_check (/DESIGN/STEEL/
KDS-41-30-2022/CODE-ANAL, PERFORM_TYPE=ALL) and perform_column_check
(/DESIGN/RC/KDS-41-20-2022/CC-ANAL, same) both answered in under 2
seconds with clean, informative errors ("failed:LoadCombination" and
"failed:Material, Section, LoadCombination, ElementType, Axis,
SectionType, SectionData, Rebar, Analysis, BeamData" respectively) —
neither ever completed a full check, since the synthetic model has no
active load combination and (CC-ANAL's case) no concrete/rebar data at
all. This does not clear the historical hang risk — the past hangs
happened on a real RC model with actual concrete/rebar data actually
reaching the design-check computation, a code path this synthetic model
never got close to. Follow-up CC-TABLE/CODE-TABLE calls also failed
cleanly ("MIDAS GEN NX second query is wrong"), no hang either.
Two open questions parked, not resolved:
- /ope/STORPROP 404'd a 3rd time even with real /db/STOR data present
and FORMAT="Default" (the manual's own worked-example value) — ruling
out both of the two leading hypotheses from the earlier attempt. Genuine
cause still unknown; documented in ope.py's StoryPropertiesArgument
docstring.
- /ope/LCOM-GEN (CODE_SELECTION="CONCRETE") still fails "Wrong
Field" even with every documented-required field for that body
present. Not pursued further since LCOM-CONC already covers the same
design body successfully; documented in generate_load_combination_general's
docstring.
- A second /ope/LCOM-STEEL call (regenerating combinations on a
fresh model iteration, this time to feed CODE-ANAL) failed
("Set_DefaultLoadComb failed; check CheckLcom history...") after an
earlier identical-shaped call had succeeded cleanly in a prior model
iteration. Not reconciled — possibly some hidden state dependency,
possibly unrelated to the payload at all.
Coverage after this whole multi-crash session: 328/398 live-verified
(+24 from the day's starting 304), covering /ope/*, /view/*,
/post/TABLE's two analysis categories, and a first live pass at the
*-ANAL design-check family.
2026-07-30 (later still) — 🎉 POST /db/NMAS no longer crashes on Gen NX v2.1, build 07/30/2026 either¶
User reported MIDASIT shipped a Gen NX patch too and asked for the same live re-verification already done for Civil. New build confirmed via the About dialog: MIDAS GEN NX 2026 (v2.1), build 07/30/2026 — one day newer than every Gen build tested earlier this session (07/28/2026).
Mid-test wrinkle, not a defect: the session originally connected via a
saved key started answering 404 "client does not exist" /
verify_connection() → "disconnected" — genuinely different from every
other disconnect signature seen this session (which always showed
"connected" while /db/* timed out). Turned out to be a different
machine entirely: the user runs Gen NX on two separate PCs (A and B)
with independent MAPI keys, and the originally-saved key was PC B's,
which had gone offline for an unrelated reason. The user supplied PC A's
key instead, which connected cleanly. .env updated with a note about
the two-PC setup so a future stale-key confusion is diagnosed faster.
On PC A's freshly patched session (0 nodes, scratch document): reproduced
the exact historical trigger — two nodes, two POST /db/NMAS calls each
omitting rmX/rmY/rmZ, the shape that reliably killed both products
before the fix. Both calls succeeded in 0.1s each, verify_connection()
stayed "connected" throughout, and the follow-up GET /db/NMAS showed
the server correctly defaulting the omitted fields to 0 itself — same
signature as Civil's confirmed fix earlier today. Test nodes/masses
cleaned up afterward; document back to empty.
Both Civil NX (v2.2, 07/29/2026) and Gen NX (v2.1, 07/30/2026) now
confirmed fixed, closing out the last open caveat from the Civil
confirmation ("Gen NX was not re-tested"). Still only one account/session
per product; independent reproduction (different account, or a third
session) would raise this from "strong evidence" to "settled". NodalMass's
SDK-side auto-fill workaround stays in place regardless — see the earlier
Civil confirmation section's reasoning, which applies identically here
(harmless once the server also defaults correctly; still needed for
anyone on an unpatched build).
2026-07-30 (last) — EDMP/USLC crashes confirmed independent of NMAS's fix, filed as MAPI-2425/MAPI-2426¶
User asked to retry today's two /ope/* crashes (EDMP, USLC) on the
same Gen NX build just confirmed to fix NMAS (v2.1, 07/30/2026), to see
whether the same patch happened to fix them too.
Both crashed again, on the first retry each. /ope/EDMP with the
identical payload from earlier (NODE_ELEMS.KEYS on 3 real BEAM
elements, TYPE=NSM, AUTO=true) timed out at 25s; verify_connection()
came back "disconnected" this time (a different signature than earlier
today's "connected"-but-blocked pattern — possibly a harder crash, or
just a different failure mode of the same underlying issue). User
confirmed Gen NX had died and restarted it. /ope/USLC (POSITION=CONC,
referencing a real load combination) crashed identically on its retry
too, "connected"-but-blocked this time. Conclusion: these are
independent defects from NMAS — the patch that fixed NMAS did nothing
for either of them.
Side finding while rebuilding for the USLC retry: /ope/LCOM-CONC and
/ope/LCOM-STEEL (auto-generate load combinations), which had worked
cleanly with a minimal payload earlier the same day, now failed with
"Set_DefaultLoadComb failed; check CheckLcom history (e.g. duplicate
LCOM names, name length, stage gate)." on a freshly-restarted Gen NX
session with a brand new empty document — not explained, mentioned in the
USLC ticket as a possibly-related data point rather than claimed as
understood. Worked around by writing the combination directly via
/db/LCOM-CONC (the DbResource, not the /ope/LCOM-CONC generator) to
get USLC a real combination name to reference.
Filed MAPI-2425 (/ope/EDMP) and MAPI-2426 (/ope/USLC) under
epic MAPI-1200, both linked "relates to" MAPI-2378 (NMAS) and to each
other, via the Atlassian MCP directly. A JQL search first ruled out an
existing duplicate — MAPI-597 ("[OPE] Change Property") looked like a
candidate but is an unrelated, already-closed 2024 ticket about a
Confluence spec page, not a crash. Unlike MAPI-2378, neither new issue
claims a root cause — both are framed honestly as "reproduced twice
(pre- and post-NMAS-patch), trigger not yet isolated," asking MIDASIT to
check server-side logs rather than asserting a fix. Also noted: mid-session,
the "disconnected" verify_connection() reading once turned out to mean
a different physical PC's Gen NX had gone offline, not a crash on the
PC actually being tested — the user runs Gen NX on two machines (A/B)
with independent MAPI keys, so a stale-looking key needs checking against
"which PC" before assuming a crash.
Also worth flagging for anyone reading this file to decide what to test next: the Gen NX session reconnected mid-troubleshooting with 710 real nodes already loaded (not the empty scratch document expected after a crash-recovery New Project) — meaning at some point the user's own real work was open in the same window this testing used. No write calls were made against that state; testing stopped as soon as it was noticed.
2026-07-31 — Civil NX re-check of today's Gen-only confirmations, plus the LCOM-CONC/STEEL/SRC 404¶
With Civil NX v2.2 (build 07/29/2026) open on the same synthetic frame model built earlier for the EDMP/USLC session, repeated the endpoints that had only been spot-checked on Gen so far:
/ope/DIVIDEELEM,MEMB,GUSTFACTOR,SSPS— all worked identically to Gen, same payload shapes./ope/LINEBMLD—TARGET.METHOD=0confirmed working on Civil too (matching the manual's worked example, same as Gen);METHOD=1not re-tried here (already unconfirmed/likely broken on Gen)./ope/AUTOMESH— same element-type restriction found on Gen holds on Civil: 4 boundary elements asBEAMsucceeded and generated realPLATEelements;TRUSSwas not re-tried (already known to fail on Gen)./view/ANGLE,ACTIVE,DISPLAY— all worked identically to Gen.- New finding:
/ope/LCOM-CONC,LCOM-STEEL,LCOM-SRCall 404 on Civil NX with the identical minimal payload ({OPTION, DGNCODE}) that generates load combinations cleanly on Gen NX. Single reproduction per endpoint, on the same synthetic frame model shape used for the successful Gen calls earlier today. Documented in each function's docstring inope.py(commit3d2695e), following the same "confirmed Gen-only, not enforced client-side" pattern already established forSTORY_PARAM/STORY_IRR_PARAM.
Then continued into the items that hadn't been attempted on Civil yet at all:
/doc/ANAL— ran clean, no save dialog this time (the model had already been saved from earlier work), matching the pre-existing 2026-07-22live_verifiedrecord for this endpoint (which already covered both products fromlive_smoke.py)./post/TABLE(Analysis Result category) —get_reaction_tableandget_beam_force_tableboth returned real non-zero rows this time (the Civil model has AUTOMESH-generated plates with real self-weight, unlike Gen's earlier all-zero-load test) — expected, not a defect./post/TABLE(Analysis Story category) —get_story_drift_tablefailed with"there was an error creating utbl. (ex PostMode ...)"on Civil. Not treated as a new defect:/db/STOR(Story data) was already confirmed Gen-only earlier in this SDK's history (db/project.py'sStory.PRODUCTS = GEN_ONLY), so a Story-table read having nothing to read on Civil is the expected consequence, not a route-level break. Not added to this endpoint's confirmed-products list.CODE-ANAL(steel) andCC-ANAL(RC column) — both re-ran cleanly on Civil, matching the Gen result: no hang, fast (<2s) informative "failed:..." errors listing missing prerequisites (load combination, material, rebar, etc.). Same caveat as the Gen finding applies: this doesn't clear the historically-hung code path, since the synthetic model still has no real concrete/rebar/load-combination data to reach it.
Civil session stayed "connected" throughout with no crashes. All of the
above recorded in docs/coverage.json, coverage steady at 398/398
implemented / 328/398 live_verified (this round only added products/method
detail to already-verified entries, no new endpoints crossed into
live_verified for the first time).
2026-07-31 (later) — a real production cable-stayed bridge on Civil NX closes 28 read-only gaps¶
The user loaded a real production model into the same Civil NX session
(v2.2, build 07/29/2026): 273 nodes, 278 elements (202 TENSTR cable-type
plus 76 BEAM), no analysis run yet, no design/rebar data. This is genuine
user data, not a scratch file — no write/execute call with any known crash
or hang risk was made against it; everything below is a GET-shaped POST
that only reads or reports "nothing to show yet."
Closed 28 of the previously-unreachable /post//DESIGN read-only
table endpoints (coverage 328 → 356/398) by calling each with a minimal
valid argument and confirming the route + argument shape are accepted and
the error handling is clean:
/post/PM,STEELCODECHECK,BEAMDESIGNFORCES,COLUMNDESIGNFORCES,BRACEDESIGNFORCES,WALLDESIGNFORCES,STEELMEMBERDESIGNFORCES,SRCBEAMDESIGNFORCES,SRCCOLUMNDESIGNFORCES,COLDFORMEDSTEELMEMBERDESIGNFORCES(post/design.py) — all answered"Please Check/perform Analysis"or"there was an error creating utbl"./DESIGN/STEEL/KDS-41-30-2022/CODE-TABLEandTABLE— same "please perform analysis" / utbl error pattern./DESIGN/RC/KDS-41-20-2022/BD-TABLE,CD-TABLE,BRD-TABLE,WD-TABLE,HCD-TABLE(design_forces.py) — all "please perform analysis"./DESIGN/RC/KDS-41-20-2022/BC-TABLE,CC-TABLE,BRC-TABLE(checks.py) initially failed"Wrong Field"with onlyELEMSset — turned outTABLE_TYPE("MEMB") is required and I'd omitted it; once added, all three answered cleanly with "please perform analysis". Not an SDK defect, just an easy-to-miss required field matchingCODE-TABLE's own shape./DESIGN/RC/KDS-41-20-2022/WC-TABLE,TABLE(x3, one physical route shared by Column/Brace/Beam Design Forces per its own docstring) — same pattern./DESIGN/SRC/AIK-SRC2K/BC-TABLE,CC-TABLE,TABLE(x2, Beam/Column Design Forces sharing one route) — same pattern.
Session stayed "connected" throughout, no crashes, no hangs — but note
none of these ever returned real populated data, only clean
"nothing to check yet" errors, since the model has no analysis/design
results. Recorded in coverage.json as confirming route, argument shape,
and error-handling only; flagged for a follow-up pass once real analysis
and design results exist on a model like this.
/ope/STORPROP re-confirmed 404 on Civil too (previously only tried
on Gen), this time against real data, not a synthetic model. Consistent
with /db/STOR (Story) already being Gen-only, though the original Gen
404 itself was never root-caused, so "this whole route is Gen-only" stays
a plausible guess, not a confirmed fact. Noted in StoryPropertiesArgument's
docstring.
Explicitly NOT touched, and why — all of these need either a real file path on the NX host machine or the user's explicit go-ahead given this is now real production data, neither of which was available/asked for in this round:
/doc/OPEN/CLOSE/SAVE/SAVEAS/STAGAS/IMPORT/IMPORTMXT/EXPORT/EXPORTMXT— need a safe file path from the user./view/CAPTURE/PRECAPTURE,*-REPORTacross all three design families,DREULT,CDESIGN— all requireEXPORT_PATH(write a file on the NX machine); same blocker as/doc/*.- All remaining
*-ANAL"perform" endpoints (BD/CD/BRD/WD/HCD-ANAL,BC/BRC/WC-ANALunder RC-KDS,BC-ANAL/CC-ANALunder SRC) — this family has documented hang history (perform_column_check/perform_wall_design, see earlier 2026-07-25 sections); not run against real data without asking first. /DESIGN/SRC/AIK-SRC2K/OCHECK— iterative re-analysis/optimization, flagged in its own docstring as "never independently tested," at least as much risk as the*-ANALfamily./DESIGN/SRC/AIK-SRC2K/DSRC— PUT/DELETE-only config write (sets the active SRC design code); skipped rather than mutate a real model's settings without asking, even though it's likely low-impact./ope/LCOM-GEN(still "Wrong Field" from 2026-07-30, not re-attempted: it's a write, and re-debugging it means adding combos to real data) and/ope/GSBG(needs a load combination with real analysis results plus a Bridge Group defined — this model has neither yet:/db/STLDshows only one"Self Weight"case, and there's no dedicated bridge-group/db/*resource in this SDK to create one via API)./ope/EDMP/USLC— already known Gen NX crashers (MAPI-2425/2426), not retried here at all (different product, and real data regardless).
2026-07-31 (later still) — a real modeling defect found and fixed on the bridge, then a real /doc/ANAL confirms post-analysis progression¶
Off to the side from SDK verification: the user spotted that the deck in
the 3D view looked like it was "standing up" instead of lying flat, and
asked me to check. Node-coordinate inspection showed the deck centerline
itself was correctly horizontal (X 0-657m, Y 5.5m constant, Z gently
curving 20.7-25.2m) with ANGLE: 0 on every deck BEAM element (203-254)
— no rotation/orientation bug. The real cause turned out to be /db/SECT
id 3 ("Deck"), a Box section with H and B swapped: vSIZE was
[11, 1.6, 0.04, 0.04, ...] (11m tall, 1.6m wide) instead of the clearly
intended [1.6, 11, ...] (1.6m tall, 11m wide — matching the deck's real
11m Y-width exactly). Fixed live with the user's consent:
PUT /db/SECTwithvSIZE[0]/vSIZE[1]swapped succeeded, but theSTIFFsub-object (Area/Asy/Asz/RXX/RYY/RZZ — note: despite theRprefix these are moment-of-inertia-type values, not radii of gyration; the manual doesn't document thisSTIFFresponse shape at all for/db/SECT) was NOT recomputed by the PUT — it stayed stale at the old H=11/B=1.6 numbers even after the geometry changed.- The user opened the Section Data dialog in the Civil NX GUI and clicked
"Calc. Section Properties", which recalculated Area/Asy/Asz/Ixx/Iyy/Izz
correctly for the new H=1.6/B=11 shape in the dialog only — a
follow-up
GET /db/SECT/3still showed the old stale values until the user clicked OK to actually commit the dialog. - After OK,
GET /db/SECT/3confirmed the new values saved server-side (AREA: 1.0016,RXX: 1.868,RYY: 0.559,RZZ: 12.525,CYP/CYM: 5.5,CZP/CZM: 0.8, matching H=1.6/B=11 exactly).
Takeaway for this SDK: PUT /db/SECT on a SECTTYPE: "VALUE" box
section only stores the raw dimensions — it does not auto-derive the
STIFF mechanical properties from them. Anything writing /db/SECT
sections programmatically needs to either compute STIFF itself before
sending, or have a human recalculate via the GUI afterward — there's no
API-callable "recalculate now" action found in this chapter.
With the corrected section in place, the user asked to re-run analysis
and sanity-check the results. POST /doc/ANAL completed in 5s (no crash).
This model uses Construction Stage Analysis (/db/STAG has 8 stages,
CS0-CS7) rather than plain static load cases — get_reaction_table/
get_cable_force_table/get_displacement_table all returned {"message":
""} (empty) for ordinary load_case_names like "Self Weight" or
"Self Weight(CS)" until switching to load_case_names=["Summation(CS)"]
with opt_cs=True and stage_step=["CS7:001(last)"] (final stage, per
the manual's "CS1:001(first)"/"CS1:002(last)" format at
19_POST_AnalysisResult_1.md:65) — then all three returned real data:
symmetric reactions (844.4 tonf total, matching the bridge's mirror
symmetry exactly), all 202 cable elements in tension (none went slack/
compression after the section fix), and DZ displacement up to 2.37m
(flagged to the user as needing their own engineering judgment — cumulative
construction-stage displacement isn't directly comparable to a simple
span/400 serviceability check).
Bonus finding while retesting post/design.py after the real analysis
existed: get_pm_interaction_diagram's error changed from "Please
Check Analysis" (pre-analysis) to "Please Check RC Design Code"
(post-analysis) — confirms the route correctly progresses through its
precondition chain rather than being stuck. get_steel_code_check
stopped erroring entirely post-analysis, now answering the "empty"
shape (nothing to report since no code check has been run, not a
failure). BEAMDESIGNFORCES/COLUMNDESIGNFORCES/
STEELMEMBERDESIGNFORCES still hit "there was an error creating utbl"
even post-analysis — these apparently need a load combination too, not
just an analysis result, which this model doesn't have yet (only the one
"Self Weight" load case exists, no LCOM-*).
2026-07-31 (later still) — 19 file-path/ANAL-family endpoints closed, then a new crash: OCHECK¶
With the user's go-ahead and a real safe path on the NX host machine
(E:\MIDAS PROGRAM\temp), closed 19 more endpoints on the real bridge
model (coverage 356 → 375/398):
/doc/* lifecycle, verified as real round-trips, not just trusted
"command complete" responses:
save_as()to a new.mcbxfile, thenopen_project()on that same path — reopened with all 273 nodes intact, confirming the write actually happened (per this SDK's ownsave_asdocstring warning that a rejected path still answers "command complete").close_project()— succeeded, and a follow-upGET /db/NODEcorrectly answered"The project is not opened"(clean, not a hang) — thenopen_project()on the same file restored a fully working session.save()— succeeded (against the test file, not the original, since the open-document context had already switched via theopen_project()round-trip above).export_json()/export_mxt()— both answered command-complete; not independently read back — see below for whyIMPORTwasn't attempted, which is the same reason a round-trip verification wasn't either.stage_as()(/doc/STAGAS) — took 3 tries:.mcbxextension failed ("Please check the file name or extension"), then the post/TABLE-style qualified stage name"CS7:001(last)"failed ("Please specify the correct stage name"), then the plain stage name"CS7"(matching/db/STAG's ownNAMEfield) with a legacy.mcbextension succeeded. Both failures were my own wrong guesses, not a live defect — the manual's own worked example already shows exactly this shape (STAGE_STEP: "Fase1",EXPORT_PATH: "...FASE1.mcb"). Updatedstage_as()'s docstring indoc.pyso the next person doesn't make the same guess from the parameter name alone.
/view/CAPTURE and PRECAPTURE: capture() with just EXPORT_PATH
succeeded cleanly. precapture() (fiber-section diagram) answered a
clean "second query is wrong" — this model has no fiber-modeled
sections, so the argument shape is confirmed but never produced real
data.
Explicitly still skipped: /doc/IMPORT/IMPORTMXT. Both are
additive/merging operations into whatever model is currently open — since
this is genuinely real production data, importing anything into it (even
a file exported from the same model) risks duplicating or corrupting
entities. These need a disposable scratch document (/doc/NEW) to test
safely, not this real bridge — not attempted this round.
The *-ANAL "perform design check" family — 10 more endpoints, all
clean, no hang: with the user's explicit go-ahead given the documented
hang history, ran BD/CD/BRD/WD/HCD-ANAL (design_forces.py),
BC/BRC/WC-ANAL (checks.py), and SRC's BC-ANAL/CC-ANAL — every one
answered "Please perform analysis" in under 1 second, no hang. Notably
this includes WD-ANAL (RC Wall Design Perform), the one endpoint in
this whole family with documented hang history on Gen NX 2026-06-23 — a
clean run here doesn't clear that history (this bridge has zero wall
elements, so it never got anywhere near the code path that hung before),
but it's one more data point of the endpoint behaving normally under
different conditions.
🔴 New crash found: POST /DESIGN/SRC/AIK-SRC2K/OCHECK. Called with
ANALYSIS_OPT.ANAL_TIME=0 and OUTPUT.MODEL_UPDATE=False (deliberately
conservative, to avoid it silently rewriting real sections if it somehow
proceeded past validation) against a model with zero SRC-eligible
sections/materials. The client-side call timed out at 25s; a same-session
GET /db/NODE timed out too — the documented "connected but /db/ blocked"
signature of a native dialog holding the session. The user checked the
screen and found a cascade of three dialogs*, distinct from every
other crash found this session (EDMP/USLC crash immediately with no
intermediate dialog):
"Error Checking Result:"listing every missing prerequisite (No Material/Section Shape/Section Type/Section Data/Load Combination/ Member Type/Element Type/Design Axis/Reinforced Bar exists for Checking) — this is the same information every other*-ANALreturns as a clean JSON error; here it's a blocking native dialog instead."Unacceptable model for optimal design."- The familiar
"[Error] Failed to disconnect the work session due to an unidentified error..."crash dialog, followed by"Program will be closed due to an unexpected problem"with an auto-recovery file path (E:\MIDAS PROGRAM\temp\sdk_test_save_restore.mcb).
After the user restarted Civil NX (re-execute → New Project → close,
per the dialog's own instructions) and reconnected with the same MAPI
key, the session recovered fully: 273 nodes intact, and /db/SECT/3's
H/B fix from earlier this session (vSIZE: [1.6, 11, ...]) survived —
no data loss. Not yet filed to MIDASIT Jira — needs the user's
explicit go-ahead first, per the standing rule from the EDMP/USLC
filing earlier this session (see MAPI-2425/2426).
Coverage after this round: 375/398 implemented+verified; remaining gap is
/doc/IMPORT/IMPORTMXT (needs a scratch document), /ope/LCOM-GEN/
STORPROP/GSBG (unresolved or blocked on prerequisites not available
via API), OCHECK (now confirmed a crasher, not "verified working"),
DSRC (config write, skipped for consent), and the *-REPORT/DREULT/
CDESIGN family (still need real design results to test meaningfully,
which no *-ANAL on this model has produced — every one failed the
"please perform analysis"/prerequisite check, since this bridge has no
RC design code or load combination configured).
2026-07-31 (last) — Civil NX coverage closed out to 392/398: *-REPORT family, DSRC, IMPORT/IMPORTMXT, and a routing-level confirmation for LCOM-*/STORPROP¶
Closed the rest of what was tractable on Civil NX this session (375 → 392/398):
All 12 remaining *-REPORT endpoints plus DREULT/CDESIGN (Steel
CODE-REPORT, RC-KDS BD/CD/BRD/WD/HCD/BC/CC/BRC/WC-REPORT, SRC
BC/CC-REPORT) answered "Please perform analysis" cleanly with the
same E:\MIDAS PROGRAM\temp path — consistent with every *-ANAL on
this model failing the same precondition. DREULT answered "It's
failed, Post Mode is not available"; CDESIGN answered "It's not
found Figure Name" (needs a pre-existing named smart-report figure
config this model doesn't have) — both clean, route+shape confirmed, no
crash.
/DESIGN/SRC/AIK-SRC2K/DSRC (config write) round-tripped cleanly:
PUT with DGNCODE="AIK-SRC2K" succeeded, then DELETE reverted it —
both echoed the record back, no lasting mutation left on the real model.
/doc/IMPORT/IMPORTMXT finally closed, with the user's explicit
consent to /doc/NEW a disposable scratch document (discarding the FCM
test model from the OCHECK reproduction, which was no longer needed).
On the resulting empty document: export_json() → import_json() and
export_mxt() → import_mxt() both round-tripped cleanly, staying at 0
nodes throughout — no crash, no data corruption. This is the reason
these two were deliberately skipped earlier against the real bridge
model: additive/merge operations need a throwaway target, not production
data.
/ope/LCOM-GEN retried with the simpler AIK-SRC2K schema ({OPTION,
DGNCODE: "AIK-SRC2K", RS_SCALE_FACTOR: []}, vs the KDS:2022/CONCRETE
attempt that got "Wrong Field" on Gen 2026-07-30) — 404'd outright on
Civil, a different and more definitive failure mode. Checked GET
/info/ope/LCOM-GEN and /info/ope/LCOM-CONC directly: both also 404
on Civil — the routes aren't registered on Civil NX at all, not just
failing at execution. This confirms at the routing level what was only
inferred before: the whole /ope/LCOM-* family (GEN/CONC/STEEL/
SRC) is Gen-only, closing the open question from LCOM-CONC/STEEL/
SRC's original Civil-404 finding (2026-07-30).
/ope/STORPROP got the same /info check: GET /info/ope/STORPROP
also 404s on Civil, matching the routing-level pattern above. Doesn't
explain why Gen also 404s on the same route (Gen's own /info was never
checked), so that half of the mystery stays open — but the Civil side is
now confirmed at the routing level rather than just inferred from a
failed POST.
Final Civil NX tally: 392/398. The 6 remaining are genuinely not
closeable from here:
- /ope/EDMP/USLC — confirmed Gen NX crashers (MAPI-2425/2426),
a different product's problem to retest.
- /ope/STORPROP/LCOM-GEN — routing-confirmed 404 on Civil, but why
Gen also fails (STORPROP) or exactly which field breaks (LCOM-GEN on
Gen) is still open.
- /ope/GSBG — still blocked on a Bridge Group definition, which has no
API-level way to create in this SDK's current endpoint coverage.
- /DESIGN/SRC/AIK-SRC2K/OCHECK — confirmed a genuine Civil NX crash,
reproduced twice on two different bridge models (a cable-stayed bridge
and an FCM bridge), filed as MAPI-2429 (relates to nothing yet —
linking left for the user to decide).
2026-07-31 (session end) — GSBG investigation on a real FCM bridge: two blockers understood, one still open¶
With explicit consent to "handle it freely," dug into why /ope/GSBG
(Bridge Girder Diagram Image Generation) has been unreachable all
session, using a real FCM (Free Cantilever Method) bridge model the user
opened (111 nodes, 106 BEAM elements, 17 construction stages —
Pier1/Pier2, PierTable1/2, P1Seg1-12, P2Seg1-12,
KeySeg1-3, FSM1/2, all as existing /db/GRUP entries).
Corrected an earlier wrong assumption: BRDG_GROUP is just a plain
/db/GRUP name, not a GUI-wizard-only object. docs/manual/17_DB_Bridge.md's
/db/GSBG chapter (the DB-level sibling of /ope/GSBG) requires
BODY_ELEM_GRUP_K, an integer referencing a /db/GRUP group — proving
the "Bridge Girder Group" concept is exactly the general-purpose
Structure Group table this SDK already exposes as StructureGroup
(db/project.py, fully POST/PUT-capable). Split this model's 106 BEAM
elements into 82 horizontal (girder, ids 1-82, constant Z between
segment endpoints) vs 24 vertical (pier, ids 83-106, constant X) by node
geometry, then created a new group:
This succeeded and is now a permanent addition to this real model — worth
noting since StructureGroup.PRODUCTS/METHODS is NO_DELETE_METHODS,
so it can't be removed via this SDK; only through the Civil NX GUI if the
user wants it gone.
First blocker, understood but not solvable via API alone: "post mode
is required." Passing BRDG_GROUP="Girder_All" got past the
group-not-found stage into this error (matching DREULT's "Post Mode is
not available" seen earlier on the cable-stayed bridge — the same
underlying gate, different wording). Ran a real /doc/ANAL first (46.6s
for this 17-stage FCM model, no crash), then tried set_result_graphic()
(/view/RESULTGRAPHIC, TYPE: "CS", NAME: "Summation") — that itself
succeeded, but did not clear the "post mode" gate for GSBG. Only
the user manually clicking the "Post" tab in the Civil NX GUI did. No
API-only path into this mode was found this session.
Second blocker, reproduced but not root-caused: "Final/PostCS stage is
not supported." Once Post Mode was manually enabled, every GSBG call
hit this new error — and it did not change across STAGE_LIST values
CS1/CS2/CS3/CS16/CS17 (the model's real final stage) or
LC_NAME values "Self"/"Self(CS)"/"Summation(CS)". Since varying
the documented parameters had zero effect, this reads like leftover
document state — plausibly the earlier set_result_graphic() call's
NAME: "Summation" (itself a "Final/PostCS" aggregate result type)
stuck the document in a mode GSBG refuses to diagram from, rather than
anything wrong with the GSBG call's own arguments. Not confirmed before
the session ended.
Net effect: GSBG went from "can't even test it" to "two of three
blockers understood, one open." Recorded as still unverified in
coverage.json — a real success would need starting fresh (undo the
Summation result-graphic state, or open yet another session) and
confirming whether a plain single-stage set_result_graphic() call
avoids the "Final/PostCS" error.
2026-08-01 — Gen NX re-check of the 3 unresolved /ope/* cases (STORPROP, LCOM-GEN, OCHECK)¶
Connected to a fresh Gen NX session (PC A, user sjj0507@midasit.com) to
follow up on the endpoints left "no live_verified" in coverage.json,
skipping the two already-filed crashers (EDMP/USLC, MAPI-2425/2426)
and the blocked GSBG.
/ope/STORPROP — 4th reproduction, still 404. Same FORMAT="Default",
PLACE=4 payload that 404'd on 2026-07-30. Still fully unexplained; not
worth another blind retry without checking Gen's own /info/ope/STORPROP
(only Civil's has been checked, see the 2026-07-31 entry above).
/ope/LCOM-GEN — new data point, still broken. Only the KDS:2022/
CONCRETE schema had been tried on Gen before (2026-07-30, "Wrong Field").
Today's minimal AIK-SRC2K schema ({OPTION, DGNCODE: "AIK-SRC2K",
RS_SCALE_FACTOR: []}) reached the server (200, not 404 — confirms the
Gen route is genuinely live, unlike Civil's routing-level 404) but also
answered "Wrong Field". Both schema shapes now fail the same way on Gen;
root cause still unknown.
/DESIGN/SRC/AIK-SRC2K/OCHECK — attempted, but not a real repro of the
Civil crash. The open Gen document had zero sections (/db/SECT
empty) — not the "sections exist, none SRC-eligible" shape that crashed
Civil NX on 2026-07-31 (MAPI-2429). A fabricated SECT_NO: 1 got a
clean "Section 1 does not exist." JSON error; a follow-up GET /db/NODE
confirmed the session stayed alive (0 nodes, matching the empty document).
This does not clear Gen of the same crash risk — it never reached the
code path that crashed Civil, since that path needs a real, existing,
non-SRC-eligible section to validate against. Re-test needed against a
real Gen model with actual section data before drawing any conclusion
either way.
Net: no new crashes, no new working endpoints — just narrowed what's
already known. All three remain open. Nothing filed to Jira from this
round (nothing new to report — LCOM-GEN's finding refines a symptom
already implicitly covered by generate_load_combination_general's own
docstring, not a new bug).
2026-08-01 (later) — real Gen NX apartment model retest of OCHECK crashes; a new crash found on KDS-41-20-2022/TABLE (CD-TABLE), filed as MAPI-2431¶
Following up on the note above that the empty-document OCHECK attempt didn't clear Gen of crash risk: reconnected to a fresh Gen NX session (new MAPI key) with the user's real production "apartment" model open (14,027 nodes, 476 sections) to retest against actual section data.
/DESIGN/SRC/AIK-SRC2K/OCHECK crashed Gen NX on the real model.
verify_connection() kept answering "connected" while every /db/*
call timed out, then the NX process itself died — the same signature
previously seen on Civil (MAPI-2429). Recovery cycle (dismiss dialogs
→ relaunch → New Project → close → reconnect with the same MAPI key)
completed with no data loss: post-recovery /db/NODE//db/SECT
counts matched pre-crash exactly (14,027 / 476). This is the first
confirmation that the crash-recovery-without-data-loss pattern, well
established on Civil, also holds for Gen NX.
Then a second, different crash: POST /DESIGN/RC/KDS-41-20-2022/TABLE
(Column Design Forces, get_column_design_forces_table /
TABLE_TYPE_COLUMN_DESIGN_FORCES). Tried as the next step in the
RC/SRC design-code verification plan, on the assumption that a TABLE
read-back endpoint would be safe (no prior crash history for any
*-TABLE case) — that assumption was wrong. The very first call
produced the identical "connected but every /db/* call times out,
then the process dies" signature. Recovered the same way, again with
no data loss once the user reopened the apartment model.
To rule out leftover state from the OCHECK crash/recovery cycle as a
confound, reproduced independently: recovered to a freshly-created,
completely empty Gen document and issued the same CD-TABLE call in
isolation. Same crash signature, same recovery, same no-data-loss
outcome (trivially, since the document was empty). Two independent
reproductions (real populated model, isolated empty model) — meets this
project's bar for "confirmed," not just "unconfirmed single repro."
Filed as MAPI-2431 (build: MIDAS Gen NX 2026 (v2.1), Build
07/30/2026), under epic MAPI-1200. Not linked to MAPI-2429/OCHECK —
different endpoint, no evidence of a shared cause. get_brace_design_forces_table/get_beam_design_forces_table share the same
underlying TABLE endpoint/helper and were not independently tested;
flagged as equally at risk in their docstrings until tested.
Net for this session: two real crashes confirmed on Gen NX
(OCHECK via MAPI-2429's Civil finding now also reproduced on Gen;
CD-TABLE newly found and filed as MAPI-2431), zero data loss across
both. Further crash-risk testing on the real apartment model paused
per the user pending explicit re-confirmation — the working assumption
that *-TABLE/*-REPORT reads are inherently safe no longer holds and
each new candidate needs to be treated as a possible crasher until
tested.
2026-08-07 — patch verification: MAPI-2425/MAPI-2426 fixed on both products; the predicted get_beam_design_forces_table risk confirmed; MAPI-2429 closed as "not a defect"¶
Followed up on a fresh, previously-unseen crash found earlier the same
session (get_beam_design_forces_table, POST /post/TABLE,
TABLE_TYPE_BEAM_DESIGN_FORCES — hung Gen NX build 07/30/2026 on the
very first call of a post/* read sweep, ~4 minutes of timeouts before
the session died). The user then patched both products — About dialogs
confirmed MIDAS GEN NX 2026 (v2.1), Build 08/06/2026 (patch v975,
see below) and MIDAS CIVIL NX 2026 (v2.2), Build 08/06/2026 — and
asked to check whether previously-reported issues were resolved, and
whether the patch introduced anything new.
Checked MIDASIT's internal Jira for what changed. MAPI-2425
(/ope/EDMP) and MAPI-2426 (/ope/USLC) — both filed 2026-07-30, both
originally Gen-NX-only reports — were marked DONE at 09:13-09:15 the
same morning the user patched, linked to a shared fix build
(GEN_NX_US_D260806_T0910_N3547_r_b1_MR.zip, v975). Root cause per the
vendor comment on MAPI-2425: AUTO: true internally still read
PARAMETER even though the manual only documents it as required when
AUTO: false; with AUTO: true and no PARAMETER, the code used the
missing value directly instead of guarding it, crashing the product. The
fix changes that path to return a proper error response instead of
crashing — it is not a requiredness change to PARAMETER itself, so
this SDK's existing docstring (PARAMETER: ... required if AUTO=false —
matching the manual exactly) needed no correction.
MAPI-2429 (/DESIGN/SRC/AIK-SRC2K/OCHECK crash, filed against both
products) was closed the day before as "결함 아님" (not a defect) —
not because it doesn't crash, but because the endpoint itself is an
unofficial, paused-mid-development API that MIDASIT is renaming to a
/TEMP/DESIGN/... prefix (also affects STEEL/KDS-41-30-2022/OCHECK
and SRC/AIK-SRC2K/DCHECK). No fix timeline; still crash-prone by
design, not by accident.
Retested EDMP/USLC live, on both products, with the exact
crash-reproduction payloads from the original reports. Per the user's
standing permission to build disposable models for this ("모델은 안
중요해, 필요하면 더미 만들어서 테스트해도 됨"), built a minimal model on
each product from empty (Material "C24"/KS01(RC), Section
400×400 DBUSER, two Nodes, one BEAM Element, one StaticLoadCase
"DL", one LoadCombinationConcrete "cLCB1") and issued:
POST /ope/EDMP
{"Argument": {"NODE_ELEMS": {"KEYS": [1]}, "TYPE": "NSM", "AUTO": true,
"CODE": "Korean Standard", "H_VS": 0.5}}
Both products, both calls: no crash. EDMP now answers
MidasResultError("Unknown Error") — a clean rejection, not a hang.
USLC now answers {"message": "MIDAS <product> NX command complete"}
— an actual success. verify_connection() confirmed healthy after every
call, on both Gen and Civil. Neither crash report was originally filed
against Civil NX (both said "Gen NX 세션 종료" only) — tested there
anyway since the fix build is shared, and it holds on Civil too.
The get_beam_design_forces_table crash from earlier this session is
not a new, unfiled bug — it's the risk the 2026-08-01 entry above
already flagged and left untested. That entry (MAPI-2431, Column
Design Forces) noted: "get_brace_design_forces_table/
get_beam_design_forces_table share the same underlying TABLE
endpoint/helper and were not independently tested; flagged as equally at
risk." post/base.py's get_table() confirms this at the code level —
every post.design.get_*_design_forces_table() function, Beam included,
routes through the same POST /post/TABLE call with a different
TABLE_TYPE. This session's Beam crash is that prediction landing, on
build 07/30/2026 (pre-patch) — same failure signature as MAPI-2431
(long timeout, then every /db/* call fails, then the process dies).
Whether the 08/06 patch fixes this specific TABLE_TYPE the way it fixed
EDMP/USLC is untested — retesting it means deliberately repeating a
call already known to hang, so it stays paused pending the user's
explicit go-ahead, per "위험한 것은 마지막에."
Net: two of three previously-reported crashes (EDMP/USLC) confirmed
fixed on both products by the same patch; the third (OCHECK) confirmed
not fixed and not going to be (unofficial API); MAPI-2431's Column
Design Forces crash remains unresolved (IN PROGRESS) and its sibling
Beam Design Forces crash is now empirically confirmed rather than just
predicted. No evidence of new, undocumented endpoints in this patch —
checked both scripts/check_manual_drift.py (has_diff: false) and a
7-day Jira sweep of [MIDAS API]-tagged issues (nothing beyond the six
already tracked here).
2026-08-07 (later) — MAPI-2431 re-tested post-patch: still crashes, and MIDASIT couldn't reproduce it on their side¶
MIDASIT replied on MAPI-2431 saying the same call worked fine in their
environment and asked for the model used to test it. Rather than share a
model file, re-tested two cases live on Gen NX (same patch build, v975,
build 08/06/2026) to characterize exactly when it does and doesn't crash:
Case 1 — the currently-open real model, no design run yet: no crash.
POST /DESIGN/RC/KDS-41-20-2022/TABLE with
{"Argument": {"TABLE_NAME": "", "TABLE_TYPE": "COLUMNDESIGNFORCES"}}
answered a clean empty {}, and a follow-up verify_connection()/
GET /db/NODE confirmed the session stayed healthy. This matches what
MIDASIT saw on their side.
Case 2 — a completely blank document (/doc/NEW, confirmed 0 nodes via
GET /db/NODE): crashes, same signature as before. The same call got no
response; verify_connection() then answered "status": "disconnected"
(notably accurate this time, not the usual false "connected") and
GET /db/NODE failed 404 client does not exist. Gen NX needed a
restart.
This reconciles with the 2026-08-01 entry above, which also crashed on
both a real populated model (14,027 nodes, real section data) and an
isolated empty document — so the empty-document repro isn't new. What's
new is that today's small real model (63 nodes) did not crash, where
the much larger apartment model on 08-01 did. That's not enough evidence
to conclude the patch fixed anything for real models — the two "real
model" tests differ in more than just the patch (node count, whether any
design/analysis had been run, section data present or not) — but it does
mean the crash isn't purely a function of "real model vs. empty model."
The one variable that reproduces it reliably across every test so far,
pre- and post-patch, is zero design data present — trivially true for
a blank document, and possibly also true of the small 08-07 test model if
it turns out to have sections without any run design check. Reported both
cases back on the ticket with the exact repro steps (/doc/NEW then the
call above, no model file needed) since that's a strictly easier repro
path for MIDASIT than sharing a model.
Still IN PROGRESS, unresolved as of this entry.
2026-08-07 (later still) — the TABLE//post/TABLE design-forces crash family is Gen-NX-only: full Civil NX sweep clean, plus a 43/43 live_crud_check.py reconfirmation¶
Following the MAPI-2431 re-test above, checked whether the crash family
(everything sharing post.design's /post/TABLE helper, and
design.rc_kds.checks's sibling /DESIGN/RC/KDS-41-20-2022/TABLE
endpoint) is really Gen-specific, or just hadn't been tried on Civil yet.
Civil NX, same patch build (v975-equivalent, build 08/06/2026), against a real model (111 nodes, no design run yet):
/post/TABLE, all 8TABLE_TYPEvalues inpost.design(BEAMDESIGNFORCES,COLUMNDESIGNFORCES,BRACEDESIGNFORCES,WALLDESIGNFORCES,STEELMEMBERDESIGNFORCES,SRCBEAMDESIGNFORCES,SRCCOLUMNDESIGNFORCES,COLDFORMEDSTEELMEMBERDESIGNFORCES) — every single one answered a clean200-with-error-body"there was an error creating utbl (ex PostMode ...)". No hang, no crash;verify_connection()/GET /db/NODEconfirmed the session healthy after each call, node count unchanged (111) throughout./DESIGN/RC/KDS-41-20-2022/TABLE, the two not yet covered (BEAMDESIGNFORCES,BRACEDESIGNFORCES—COLUMNDESIGNFORCESwas already confirmed clean here back on 2026-07-31) — same cleanPostModeerror, no crash.
Net: 11 of 11 tested combinations are clean on Civil NX; the only
confirmed crashes in this whole family remain the two already filed on
Gen NX (MAPI-2431's Column Design Forces via the KDS-41-20-2022
endpoint, and this session's Beam Design Forces via /post/TABLE, both
reproduced again post-patch). The remaining Gen-side combinations
(Brace/Wall/Steel/SRC/Cold-Formed via /post/TABLE, Brace via the KDS
endpoint) are still untested on Gen and are documented as "equally at
risk" rather than confirmed either way — this crash family looks
Gen-specific so far, but that's not the same claim as "safe on Gen for
the untested types."
Separately, ran scripts/live_crud_check.py --product civil (all
tiers, no --include-crashers) against this same patch build: 43/43
resources completed a full create→read→update→read→delete→read round
trip, no failures. This is a reconfirmation, not a new finding — all
43 cases were already confirmed=True as of 2026-07-29 per the script's
own tracking — but it's the first time they've all been re-run together
against the 08/06/2026 patch build, and nothing regressed. Note this
script creates its own scratch document via /doc/NEW, so it discarded
whatever was open beforehand (with the user's explicit go-ahead, since
that document didn't matter).
2026-08-07 (last) — full Civil NX DbResource GET sweep closes most of the read-coverage gap; GEN_ONLY reconfirmed unchanged¶
Prompted by a moment of confusion: after a run of Civil-specific findings, it looked like Civil NX coverage might be "done." It wasn't — at the time, only 195/399 endpoints had ever been live-verified on Civil at all (most of this project's live sessions have skewed Gen-heavy). Ran the actual numbers, corrected the misunderstanding, then closed as much of the real gap as a single sweep reasonably could.
scripts/live_readonly_sweep.py --product civil --record-coverage
against build 08/06/2026 (confirmed via a Help > About screenshot: MIDAS
CIVIL NX 2026 (v2.2), Build: 08/06/2026) swept all 282 GET-capable
DbResource classes (everything in db/*, plus
design.rc_kds.rebar/design.rc_kds.setup/design.steel_kds/
design.src_aiksrc2k's DbResource-based classes — the plain-function
TABLE/ANAL/REPORT endpoints in design.rc_kds.checks and
doc.py/ope.py/post/* aren't covered by this tool at all, since it
only enumerates DbResource subclasses). All 282 answered ok — no
failures, no crashes, session healthy throughout.
The script's own --record-coverage only writes a new live_verified
entry where none exists yet, so it undercounted: most of these 282 already
had a Gen-only live_verified entry from an earlier session, and the
script correctly declined to touch those (its docstring says so
explicitly — an existing entry is never overwritten). Wrote a one-off
merge script instead: for every endpoint this sweep confirmed ok, if its
existing live_verified.products didn't already include "civil", add
it, append a short note, and cite this build. Matched primarily by
(endpoint, module); a residual 21 entries use a wildcard module string
("midas_nx.db.properties.*") in coverage.json instead of the real
submodule, so those were matched by endpoint alone (checked for
uniqueness first) in a second pass. 172 endpoints gained a Civil
confirmation this way. Verified on Civil NX went 195/399 → 367/399.
Separately, force-tested all 20 GEN_ONLY-tagged DbResource classes
against Civil NX (strict_product=False, bypassing the client-side
guard that normally blocks a product mismatch) — the same check that
originally established GEN_ONLY on 2026-07-29. All 20 still 404 on
Civil, identical to the original finding: no drift across two patch
cycles. db/base.py's GEN_ONLY docstring now cites this reconfirmation
alongside the original evidence.
What's left of the 32/399 still not live-verified on Civil: almost
entirely the plain-function endpoints (doc.py/ope.py/post/*/
design.rc_kds.checks) that this sweep tool doesn't reach, several of
which overlap the Gen-only crash-risk family documented earlier this same
day (Column/Beam/Brace Design Forces and friends). Closing that remainder
needs per-module manual testing, not a single automated sweep, and was
deliberately deferred rather than rushed — see the still-open item at the
end of this file's history.
2026-08-10 — BRACEDESIGNFORCES confirmed crashing Gen NX, closing one more gap in the design-forces crash family¶
Continuing the Gen-side sweep of the post.design//post/TABLE design-
forces crash family (Column and Beam already confirmed crashing, the
rest flagged "equally at risk" but untested): tried BRACEDESIGNFORCES
against a blank /doc/NEW document on Gen NX (build 08/06/2026, same
patch already confirmed crashing for Column/Beam). Same signature: no
response, then verify_connection() → disconnected, every /db/* call
→ 404. Gen NX needed a restart.
User paused the sweep here rather than continuing through the remaining
5 candidates (WALLDESIGNFORCES, STEELMEMBERDESIGNFORCES,
SRCBEAMDESIGNFORCES, SRCCOLUMNDESIGNFORCES,
COLDFORMEDSTEELMEMBERDESIGNFORCES via /post/TABLE, plus
BRACEDESIGNFORCES via the sibling /DESIGN/RC/KDS-41-20-2022/TABLE
endpoint in design.rc_kds.checks — still untested) — each confirmed
crash costs a full Gen NX restart, and three confirmed crashes
(Column/Beam/Brace, all via the same shared /post/TABLE helper) was
judged enough evidence for the pattern without burning through the rest
one restart at a time. Remaining candidates stay documented as "equally
at risk, untested" rather than confirmed.
2026-08-10 (later) — full Gen NX DbResource GET sweep, mirroring the earlier Civil sweep¶
After confirming Gen coverage had a much bigger gap than Civil (132/399 endpoints never live-tested on Gen at all — this project's live sessions have skewed Civil-heavy since the FCM bridge model work), ran the same tool used for the Civil sweep, this time against Gen:
scripts/live_readonly_sweep.py --product gen --record-coverage
against build 08/06/2026, on a blank /doc/NEW document (post-crash
restart from the BRACEDESIGNFORCES finding above): swept all 266
GET-capable DbResource classes applicable to Gen — all 266 answered
ok, no failures, no crashes, session stayed healthy throughout and
after.
Same merge-script approach as the Civil sweep (the tool's own
--record-coverage only adds a new entry, so a one-off script added
"gen" to existing Civil-only live_verified.products wherever this
sweep confirmed it): 32 endpoints gained a Gen confirmation this way.
Verified on Gen NX went 266/399 → 299/399. A smaller jump than the
Civil sweep's 172, because most of the remaining gap here is
plain-function endpoints (doc.py/ope.py/post/*/
design.rc_kds.checks/design.rc_kds.design_forces/
design.src_aiksrc2k's ANAL/TABLE/REPORT triads) this tool doesn't
reach — including the crash-risk family from earlier today, which stays
untouched by this GET-only sweep by design.
Explicitly paused here rather than continuing into those plain-function
modules: several overlap the confirmed Gen crash-risk family, and the
RC/SRC/Steel *-ANAL triads have their own independent Gen-hang history
(see the 2026-08-01 entries above) — closing the remainder needs
per-module manual testing with explicit go-ahead each time, not another
single automated sweep.
2026-08-10 (last) — non-crash-family ANAL/TABLE/REPORT/CAPTURE batch on Gen NX: 40 endpoints, no crash/hang, all recorded¶
Continuing the DbResource sweep's leftover gap: the plain-function design
chapters (design.rc_kds.design_forces, design.rc_kds.checks,
design.steel_kds, design.src_aiksrc2k, view.py) that the GET-only
sweep can't reach. Explicitly excluded from this batch: the confirmed/
at-risk Design-Forces crash family (post.design's 8, checks.py's
Column/Brace/Beam Design Forces TABLE, steel_kds.py's
TABLE=STEELMEMBERDESIGNFORCES, src_aiksrc2k.py's TABLE=
Beam/Column SRC Design Forces — all share the same *DESIGNFORCES
TABLE_TYPE naming convention as the confirmed Column/Brace/Beam crashes)
and src_aiksrc2k.py's OCHECK (confirmed crashing both products,
MAPI-2429, already recorded separately).
Correction to the earlier "22 crash-family" figure quoted mid-session:
that overcounted by treating all 14 untested checks.py entries as
crash-family, when only 3 of them (Column/Brace/Beam Design Forces) are.
The other 11 (BC/CC/BRC/WC-ANAL/TABLE/REPORT, CDESIGN) are
unrelated check/report functions — BC-ANAL/CC-ANAL were already
re-verified clean via the QuickRebar NX production tool (2026-07-25),
and WC-ANAL was independently confirmed to NOT reproduce the CC-ANAL
stall. The real crash-family total across all modules is 11 Design-Forces
functions + OCHECK, not 22.
Ran against a blank /doc/NEW document (0 nodes/0 elements, same one
left over from the DbResource sweep), in two batches:
Category A (29 calls, no ANAL/PERFORM prerequisite) — view.CAPTURE/
PRECAPTURE; design_forces.py's BD/CD/BRD/WD/HCD *-TABLE/*-REPORT
(10); checks.py's BC/CC/BRC/WC *-TABLE/*-REPORT + CDESIGN (9);
steel_kds.py's CODE-TABLE/CODE-REPORT/DREULT (3, TABLE skipped
as crash-family-adjacent); src_aiksrc2k.py's BC/CC *-TABLE/*-REPORT
(4, TABLE/OCHECK skipped). All 29 answered cleanly with informative
refusals ("Please perform analysis.", "It's not found Figure Name",
"Wrong Field", "Post Mode is not available") — no crash, no hang,
session alive after every call.
Category B (12 *-ANAL/*-PERFORM calls, 25s timeout), run only
after explicit user go-ahead given the documented Gen-hang history on
this family (WC/BC/CC/BRC-ANAL, BD/CD/BRD/WD/HCD-ANAL, CODE-ANAL, SRC
BC/CC-ANAL — WD-ANAL deliberately tested last, being the one with
confirmed historical hang evidence). All 12 answered in <1s with the same
clean "Please perform analysis." refusal, immediately followed by a
*-TABLE read-back (per the established mitigation pattern) and a
/db/NODE session-alive check — no hang anywhere, including WD-ANAL.
Plausible explanation: the historical hangs were reproduced specifically
with real rebar/analysis data to process; against 0 elements the server
has nothing to "Convert Design Results" for and fails fast instead. This
does not clear the historical hang risk for real models — it's a
second data point (blank-document-is-safe), not a fix confirmation.
Recorded via a one-off merge script (same (endpoint, module)-matching
approach as the two DbResource sweeps): 38 endpoints gained a new Gen
confirmation (2 — CC-ANAL, CODE-ANAL — already had one from an
earlier session). Verified on Gen NX went 299/399 → 337/399.
Remaining real gap on Gen: the crash-family (11 Design-Forces functions,
already characterized as crashing/at-risk, not "unverified"), OCHECK
(confirmed crashing, ditto), and doc.py's 9 file-lifecycle endpoints
(OPEN/CLOSE/SAVE/SAVEAS/STAGAS/IMPORT/IMPORTMXT/EXPORT/
EXPORTMXT — deliberately not attempted against a session with real
state at risk) plus ope.py's 3 already-investigated-and-blocked cases
(STORPROP/LCOM-GEN/GSBG).
coverage.json correction (2026-08-11): the 2026-08-10 batch's merge
script had recorded the Gen version as v2.2 (copy-pasted from Civil's
version string) on all 38 entries it touched — Gen was never on v2.2.
Corrected all 38 to MIDAS Gen NX 2026 (v2.1), build 08/06/2026, the
build actually used that session. Caught via a fresh connection check
this session: About dialog showed Gen NX 2026 (v2.1), build
08/11/2026 (user sjj0507@midasit.com).
Connection-check tooling bug, same session: the first pass of both
this Gen check and a following Civil check (About dialog: Civil NX
2026 (v2.2), build 08/11/2026) called MidasClient(..., base_url=
"https://moa-engineers.midasit.com") — overriding the default and
stripping the required /gen//civil product path segment that
build_base_url() normally appends. verify_connection() still
succeeded (its URL-strip logic in client.py tolerates either form),
masking the bug, but every /db/* call landed on a route that doesn't
exist and returned a generic 404 — misread at the time as "blank
document, 0 nodes." Re-run without the base_url override: Node.get()
→ {"message": ""} on both products, the correct zero-row shape —
so both sessions genuinely are on blank documents, same conclusion as
before, but now for the right reason. No SDK code was at fault — the
bug was in the ad hoc verification script, not client.py.
OCHECK re-tried on Civil (v2.2, build 08/11/2026) with the corrected
client, at the user's request to re-attempt Civil's known crash
(perform_src_optimal_design, docs/live_verification_notes.md's
2026-07-31 "New crash found" section). Same shape as Gen's 2026-08-01
attempt: a fabricated SECT_NO: 1 against a document with zero sections
got a clean MidasResultError — "Section 1 does not exist." — and a
follow-up Node.get() confirmed the session stayed alive
({"message": ""}). Not a repro of the 2026-07-31 crash — that
needed a real, existing, non-SRC-eligible section to reach the crashing
code path, which this blank document doesn't have. Narrows nothing new;
the crash risk on real Civil models with real sections stands as
documented.
Quick full Civil-gap sweep, same session. Of the 11 endpoints
coverage.json listed as not-yet-civil-verified, 9 turned out to
already carry a conclusive Civil finding recorded only in their
docstring/method text (not reflected in live_verified.products because
the finding is negative): STORY_PARAM/STORY_IRR_PARAM (confirmed
404, 3rd+ repro), /post/TABLE story types (confirmed error, 2 sweeps),
STORPROP/LCOM-GEN/GSBG (dead route or unmet precondition on every
product, not Civil-specific), LCOM-CONC/LCOM-STEEL/LCOM-SRC
(confirmed 404 — "the whole /ope/LCOM-* family's routes aren't
registered on Civil at all," per generate_load_combination_concrete's
existing docstring). Retesting these would only reconfirm known dead
routes/blocked preconditions, so only the 2 genuinely untested ones were
run:
/ope/STOR(Story Calculation) — 404 on Civil, first direct test against this product (SEIS_ECC/WIND_ECCboth disabled, blank doc). Extends the story family's Gen-only pattern to cover this endpoint too./view/RESULTGRAPHIC— route IS registered on Civil, unlike the story family:CURRENT_MODE=reactionforces/momentsagainst a blank doc (no analysis run) got a cleanMidasResultError,"MIDAS CIVIL NX Empty Load Case Type". Session stayed alive. Addedcivilto itslive_verified.products— same standard as the 2026-08-10 Gen batch (a clean, correctly-shaped rejection counts as verified request handling, not "no data").
Both Node.get()-checked alive after each call. No crash, no hang.
docs/coverage.json and both functions' docstrings (ope.py,
view.py) updated; ROADMAP.md regenerated.
Full write round-trip + per-endpoint PUT sweep, same session, Civil NX
v2.2 build 08/11/2026. scripts/live_smoke.py --product civil run
against the blank connected session: /doc/NEW → UNIT → MATL (C24)
→ SECT (600×600 DBUSER column) → 2×NODE → ELEM (beam) → CONS
(fixed) → STLD (DL) → BODF (self-weight) → /doc/ANAL →
reaction/displacement/beam-force tables, all 13 steps ok: true;
reaction FZ = 27.11 kN vs. 28.22 kN hand-calc, within the script's 5%
tolerance.
Followed by a dedicated PUT-only sweep (change → GET-verify → revert →
GET-verify per endpoint, on the 8 resources the smoke test had just
populated): UNIT, MATL, SECT, NODE, ELEM, CONS, STLD,
BODF — all 8 changed and reverted cleanly, confirmed via a follow-up
GET each time. One cosmetic-only observation: SECT's vSIZE readback
comes back zero-padded to a fixed 10-element array
([0.6, 0.6, 0, 0, 0, 0, 0, 0, 0, 0]) rather than echoing the 2-element
list that was sent — not a defect, just the server's storage shape for
a variable-length dimension array. Node.get() confirmed the session
alive throughout. All 8 of these endpoints were already civil-verified
at level: "write" in coverage.json (from earlier create()-only
testing) — this sweep specifically exercised update()/PUT, which
hadn't been individually confirmed before, so no new coverage entries,
just a stronger evidence base behind the existing ones.
2026-08-11 (later) — full Gen NX crash-family re-test: 15 previously-crashing/at-risk calls, 0 crashes this time¶
At the user's explicit request to re-test "everything that had crashed on
Gen," and after correcting an initial mis-framing (told the user this
would likely be inconclusive on a blank document like OCHECK — wrong:
get_beam_design_forces_table()'s own docstring already documented a
confirmed crash on a blank /doc/NEW document, so the risk was real,
not just theoretical). User re-confirmed proceeding anyway with the
corrected risk understanding.
Batch 1 — 11 previously-unverified-on-Gen crash-family functions + OCHECK,
called one at a time against the connected blank Gen session (0 nodes),
Node.get() health-checked after every single call, ready to stop
immediately on the first failure:
checks.get_brace_design_forces_table, checks.get_beam_design_forces_table,
steel_kds.get_steel_member_design_forces_table,
src_aiksrc2k.get_src_beam_design_forces_table,
src_aiksrc2k.get_src_column_design_forces_table,
post.design.get_column_design_forces_table,
post.design.get_wall_design_forces_table,
post.design.get_steel_member_design_forces_table,
post.design.get_src_beam_design_forces_table,
post.design.get_src_column_design_forces_table,
post.design.get_cold_formed_steel_member_design_forces_table, and
perform_src_optimal_design (OCHECK, zero-section precondition, same
non-repro shape as the existing 2026-08-01 finding). All 12 answered
cleanly (mostly empty-table responses), session alive throughout.
Batch 2 — the 3 already-confirmed-crashing cases, re-tested next
since "전부" (all) includes these: post.design.get_brace_design_forces_table
(BRACEDESIGNFORCES), post.design.get_beam_design_forces_table
(BEAMDESIGNFORCES), and checks.get_column_design_forces_table
(MAPI-2431 — reproduced twice independently across different models,
re-confirmed not fixed as recently as 2026-08-07, the single
strongest repro in this whole family). All 3 — including MAPI-2431 —
answered cleanly this time too. Session alive after all 3.
Total: 15 of 15 calls clean, zero crashes, zero hangs, against Gen NX 2026 v2.1, build 08/11/2026 (previous crash confirmations were on build 08/06/2026 and earlier).
This is not being called "fixed." One clean pass against a
historically-confirmed crash doesn't distinguish "the vendor fixed it"
from "the crash is intermittent/non-deterministic" — and MAPI-2431's own
history already shows non-monotonic behavior (crashed on a blank
document, clean on a 63-node real model, in the same 2026-08-07 session).
Every affected docstring and coverage.json entry now carries today's
result as an additional data point, explicitly flagged as
"reduced-but-not-cleared," not a resolution. An independent second
re-test — ideally against real, populated model data rather than a
blank document, since that's the condition never yet cleanly tested for
most of this family — is the next real step, not a documentation
close-out.
docs/coverage.json (15 entries updated/extended), ROADMAP.md
(regenerated), and the docstrings in post/design.py, checks.py,
steel_kds.py, src_aiksrc2k.py (10 functions) all updated with
today's finding. ruff check and the full test suite (701 tests) both
pass.
DSRC (SRC Design Code) tested on Gen, same session, after the Civil
session was closed. Previously deferred pending consent (config-write,
no GET to verify state either way). PUT {"DGNCODE": "AIK-SRC2K"}
against Gen NX (v2.1, build 08/11/2026, blank document) succeeded,
echoed back cleanly, session stayed healthy. Not a full round trip like
the existing 2026-07-31 Civil test (PUT + DELETE) — DELETE wasn't
re-tried this pass. coverage.json gains gen in live_verified.products;
its level was also corrected read → write (it's a PUT, not a
read, regardless of which product tested it first).
2026-08-13 — new patch on both products: connection check only¶
User applied a new patch to both products and asked to confirm both
connect, with versions recorded. verify_connection() against fresh
.env MAPI keys (last-occurrence-wins for the duplicated
MIDAS_MAPI_KEY_GEN/MIDAS_MAPI_KEY_CIVIL lines) succeeded for both:
GEN: {'user': 'sjj0507@midasit.com', 'program': 'gen', 'connectionID': 'N-pAmlMjSg', 'keyVerified': True, 'status': 'connected'}
CIVIL: {'user': 'sjj0507@midasit.com', 'program': 'civil', 'connectionID': 'WjbfiHlPSw', 'keyVerified': True, 'status': 'connected'}
About-dialog screenshots confirm the new build:
- MIDAS GEN NX 2026 (v2.1), build 08/12/2026 (previous: 08/11/2026)
- MIDAS CIVIL NX 2026 (v2.2), build 08/12/2026 (previous: 08/11/2026)
Same version numbers as the prior patch, one-day-newer build only — consistent with a routine patch rather than a version bump.
2026-08-13 (later) — /db/NMAS raw crash-trigger re-repro on Civil NX: server-side bug looks actually fixed now, not just SDK-masked¶
User asked whether the crash-family items flagged earlier in this file
were all resolved on Civil NX. Answer at the time: NMAS — SDK-safe via
the rmX/rmY/rmZ auto-fill workaround, previously confirmed the
server itself no longer crashed as of Civil NX v2.2 build 07/29/2026;
OCHECK/perform_src_optimal_design (MAPI-2429) — still open, closed
by MIDASIT as "not a defect" (unofficial paused-development API being
moved to /TEMP/...), crash risk on real models with real sections never
actually retested. User asked to proceed with NMAS first, then
explicitly asked for the raw repro (bypassing the SDK workaround) to
check the server's current state directly, accepting the session-death
risk that trigger has historically carried.
Against Civil NX v2.2, build 08/12/2026, on a blank connected document:
- SDK-safe path first (sanity check): created
Node1, thenNodalMass.create({1: {mX:1, mY:1, mZ:1}})(rm fields omitted from the call, auto-filled to0.0by the SDK before sending) — clean201, read back correctly, session alive. Cleaned up. - Raw repro, bypassing
NodalMass.create()entirely viaclient.request()directly: createdNode1 andNode2. Control call —POST /db/NMASon node 1 with all six fields explicit (mX/mY/mZ+rmX/rmY/rmZ: 0.0) — succeeded in 0.2s. Trigger call —POST /db/NMASon node 2 withrmX/rmY/rmZomitted entirely (the exact payload shape that reproduced the crash 15+ times across both products pre-fix) — succeeded in 0.1s, no timeout, no hang.GET /db/NMASread back node 2 withrmX: 0, rmY: 0, rmZ: 0— the server itself now applies the documented default, rather than reading an uninitialized value. Follow-upverify_connection()andNode.get()both confirmed the session fully responsive immediately after. Test data (NMAS+ both nodes) deleted, document back to blank, session stillconnectedthroughout and after.
Historically this exact omitted-fields call took 15-60s and then killed
the session; here it returned in under a second with a correct result.
That's a materially stronger signal than the 07-29 finding (which only
established "doesn't crash," not "applies the right default") —
consistent with MIDASIT having actually fixed the uninitialized-value
bug server-side, not just gotten lucky on one retest. Not changing
NodalMass's SDK-side workaround based on this single repro on one
account/build — the class still fills rmX/rmY/rmZ explicitly, since
that costs nothing, remains correct regardless of server state, and this
is one data point on one build, not proof the fix is universal across
every deployed installation. Only tested on Civil this session; Gen not
re-repro'd today (its own last confirmation was 2026-07-30, build
07/30/2026).
2026-08-13 (last) — OCHECK/MAPI-2429 re-repro'd on Civil NX with the real trigger shape: still crashes on the current patch¶
After the NMAS reconfirmation above, asked whether Civil's crash-family
items were "all resolved." Answer: NMAS yes (see above), OCHECK
(perform_src_optimal_design, MAPI-2429) no — MIDASIT closed it "not a
defect," no fix timeline, and the 2026-08-11 retest never actually
reached the crashing code path (zero-section document). User asked to
build a dummy model and retry properly.
Dummy model built on Civil NX v2.2, build 08/12/2026 (blank document,
confirmed empty first): Material.create (C24 concrete, KS01(RC)),
Section.create (600×600 DBUSER, SHAPE: "SB" — a real, existing,
plain concrete section, i.e. non-SRC-eligible, the exact
precondition the 2026-07-31 finding says is needed), two Nodes, one
BEAM Element referencing both. All four calls succeeded, session
alive.
perform_src_optimal_design called against it — SECT_LIST:
[{SECT_NO: 1, SECT_DB: "USER"}], ANALYSIS_OPT.ANAL_TIME: 0,
OUTPUT.MODEL_UPDATE: False (same conservative shape as the original
2026-07-31 repro). The call timed out client-side at 30s. A follow-up
GET /db/NODE immediately after returned MidasNotFoundError: 404
client does not exist — the documented "process died after connecting"
signature. User confirmed on screen: the "[Error] Failed to disconnect
the work session due to an unidentified error..." dialog, identical to
2026-07-31's crash.
Recovery, per the dialog's own instructions (OK → re-launch → New
Project → close → reconnect with the same MAPI key): reconnected
cleanly, verify_connection() → connected, same connectionID.
Node/Element/Material/Section all read back empty — the dummy
model was cleared by the New Project step, not a data-loss event; it was
disposable test data built specifically for this repro, nothing of
value was lost.
Conclusion: MAPI-2429 is confirmed still live on the current patch
(v2.2, build 08/12/2026), not fixed and not intermittent in the way
NMAS briefly was. This is a stronger data point than the 08-11
non-repro (which used the wrong precondition) and is consistent with
MIDASIT's "not a defect, no fix timeline" stance — the crash isn't
build-dependent, it's inherent to calling this unofficial,
paused-development endpoint with a real section. docs/coverage.json's
/TEMP/DESIGN/SRC/AIK-SRC2K/OCHECK entry updated with today's repro and
build. Not re-tested on Gen this session.
2026-08-13 (last) — closing the 3 "typed but never live-tested" gaps from the recent manual syncs: SWIND/SSEIS USER TYPE, renamed Story Load/Weight tables, CONCURRENT_JOINT_FORCE¶
Earlier the same day, checking what the two most recent manual-repo sync
commits (f4a55e7 08-07, 76ebda9 08-10) actually added turned up 3
items that were typed into this SDK but never independently live-tested
against the new shape: /db/SWIND//db/SSEIS's "USER TYPE" variant,
the renamed /post/TABLE STORY_LOAD_{X,Y,Z} + new STORYWEIGHT
fields, and the brand-new CONCURRENT_JOINT_FORCE table type. User asked
to test all of them, on both products, building dummy models as needed.
Gen NX (v2.1, build 08/12/2026) — built a 2-story cantilever model
from scratch on the blank connected session: Unit, Material (C24),
Section (600×600 DBUSER), 3 Nodes / 2 BEAM Elements (a
0→3.2m→6.4m column), base Constraint, 2 real Story records
("1F" at 3.2m, "RF" at 6.4m, full field set), StaticLoadCase
("DL"), SelfWeight, then /doc/ANAL. Reaction sanity-checked
(54.23 kN, self-weight of the two-segment column). Every step ok.
SWIND/SSEIS"USER TYPE": POST withSTORY_WIND_PRESSURE/SEISMIC_FORCEkeyed to"1F"/"RF"succeeded (201) for both, GET read both back correctly — the server fills in GET-only fields (ELEV/LOAD_H/LOAD_BX/LOAD_BYfor wind,WEIGHT/ELEVfor seismic) on the way out, confirming those must stay client-side-omit on write, as the docstrings already said.INHERENT_TORSION's corrected spelling (vs. the manual's own"NHERENT_TORSION"typo) confirmed as the one the server accepts.- Story Load Summary / Story Weight:
get_story_load_summary_table("X", load_case_names=["DL(ST)"])andget_story_weight_table()both returned real per-story populated rows ("1F"/"RF", correct elevations, correct self-weight distribution) — closes the "renamed TABLE_TYPE never re-tested" gap. CONCURRENT_JOINT_FORCE:additional.SET_REACTION_PARAMS = {NODE_KEY: 1, COMPONENT: "111111"},load_case_names=["DL(ST)"](then retried with 2 load cases after adding a secondStaticLoadCase"LL") — both attempts got the identical clean error"there was an error creating utbl. (ex PostMode ...)". Not a crash, not a 404 — same "PostMode" precondition family asget_wall_force_table/GSBG.
Civil NX (v2.2, build 08/12/2026) — re-confirmed /db/STOR/SWIND/
SSEIS still cleanly 404 (GEN_ONLY holds on the current patch too).
Built a fresh single-story-height cantilever column (same recipe, no
Story data — Civil doesn't have that endpoint) and tested
CONCURRENT_JOINT_FORCE there too: same request shape got a
different clean error, "No data found for the specified node
key." A follow-up retry with NODE_KEY sent as a string (instead of
int) got a worse, obviously-wrong-shape error ("second query is
wrong"), which rules out a type mismatch as the cause of the first
error — confirms the documented int type is correct and the node id
itself (which does have real reaction data, confirmed via
get_reaction_table in the same session) just isn't what this table
is looking for.
Conclusion: CONCURRENT_JOINT_FORCE's own manual note — "typically
paired with moving-load (MV:max)/(MV:min) load cases" — reads as
literal, not just typical: a plain static-load-case reaction isn't
"extreme" over anything for this table to search, on either product.
Route and request shape are now confirmed correct on both products
(no defect found); building actual moving-load (Vehicle/Lane/MV load
case) test data to exercise the real result path is future work, not
done this session.
Correction, same session: Gen's "PostMode" error is NOT the same gate
GSBG is blocked on. User asked "Gen은 현재 postmode 맞잖아?" (Gen is
already in Post mode right now, isn't it?) and shared a screenshot
proving it — the "Post-processing Mode" toolbar toggle visibly enabled,
the analysis message log showing the completed solve, on the same
2-story column model this session built. Re-ran the exact
CONCURRENT_JOINT_FORCE call with Post mode confirmed already active:
identical error, unchanged. This decouples it from the
GSBG/get_wall_force_table "post mode is required" gate documented
2026-07-31, which does clear once a human manually enables Post mode
— here it didn't, so the word "PostMode" in this particular error string
is a red herring for this table specifically. Strengthens rather than
weakens the moving-load-data hypothesis above: with the generic
Post-mode explanation ruled out, "no (MV:max)/(MV:min) result to
search over" is the more likely remaining explanation for both
products' errors. docs/coverage.json and
post/result_1.py's get_concurrent_joint_force_table docstring
corrected to not conflate this with the GSBG gate.
All dummy models left open on both sessions (not cleaned up) in case
useful for follow-up testing. docs/coverage.json updated for /db/STOR,
/db/SWIND, /db/SSEIS, and both /post/TABLE aggregate rows (ch18,
ch19); docstrings in db/project.py, db/static_loads.py,
post/pre_process.py, post/result_1.py updated to match.
2026-08-13 (last) — GSBG retest: the "post mode" gate is a reproducible fix, but the "Final/PostCS" blocker is not leftover state after all¶
Following the Civil-gap review above (which flagged /ope/GSBG as the
one genuinely open item), user opened the same real FCM bridge model
used in the original 2026-07-31 investigation and ran a full analysis,
asking to retry GSBG.
First attempt, FCM bridge (111 nodes, 106 elements), Post mode not yet
manually enabled: created a Girder_All StructureGroup covering the
82 non-pier elements (P1Seg*/P2Seg*/PierTable*/KeySeg*/FSM*
groups' union), then called generate_bridge_girder_diagram —
immediately hit the known "post mode is required" gate again, exactly
as before. Asked the user to manually click the "Post" tab.
User raised a fair question before proceeding: is it okay to test
against a real production bridge? Answered honestly — GSBG itself
has never crashed in any prior session (only clean JSON errors), and the
call doesn't write model data (only an output image file), so the risk
profile is lower than e.g. OCHECK, but not zero since this specific
combination (Post mode + a real registered Bridge Group) had never been
tried. User switched to a different, smaller real bridge model instead
(61 nodes, 52 elements, 4 construction stages CS1-CS4, groups
SG1/SG2/SG3) and confirmed Post mode active via the GUI.
With Post mode confirmed active: "post mode is required" did not
recur. Confirms the manual "Post" tab toggle is a real, reproducible
fix for that specific gate — not a fluke of the 2026-07-31 session.
But "Final/PostCS stage is not supported" came right back,
identically, across STAGE_LIST=["CS1"] (the model's first stage) with
LC_NAME = "Self Weight", "Self Weight(CS)", and "Summation(CS)",
using both a fresh Girder_All group (all 52 elements) and the model's
own pre-existing SG1 segment group as BRDG_GROUP. This session
never called set_result_graphic() before hitting the error — directly
contradicting the 2026-07-31 hypothesis that this was leftover state
from an earlier RESULTGRAPHIC call with a "Summation" load case.
That theory is now ruled out.
Tried one more thing: explicitly selecting stage CS1 as the active
result via set_result_graphic(CURRENT_MODE="beamdiagrams",
LOAD_CASE_COMB={"TYPE": "CS", "NAME": "CS1"}) — failed differently,
"Can not find load case", meaning CS1 alone isn't a valid
LOAD_CASE_COMB.NAME for that call. Didn't find the right name before
stopping (user called time on this thread — GSBG stays open for a
future session, not close-out material today).
Civil NX session stayed "connected" and responsive throughout every
attempt — no crash, no hang, on either bridge model. docs/coverage.json
gained GSBG's first live_verified entry (previously had none at all,
despite the extensive 2026-07-31 investigation never having been
synced in) recording this as still-blocked, not a success.
ope.py's generate_bridge_girder_diagram docstring updated with
today's findings.
2026-08-13 (last) — full live_crud_check.py reconfirmation on Civil NX build 08/12/2026: 43/43, no regressions¶
User asked whether Civil NX had ever had the full CRUD suite run against
it. Answer at the time: yes, all 43 cases confirmed since 2026-07-29,
last fully re-run together 2026-08-07 against build 08/06/2026 — but not
against either of the two patches since (08/11, 08/12). User gave
explicit go-ahead to run it now, understanding /doc/NEW would discard
the small 4-stage bridge model (SG1/SG2/SG3) open from the GSBG
session above (not saved, not needed further).
scripts/live_crud_check.py --product civil (all 6 tiers, no
--include-crashers — nothing is quarantined anymore): 43/43
resources completed a full create→read→update→read→delete→read round
trip, zero failures, against Civil NX 2026 v2.2, build 08/12/2026.
Covers core (10), props (7), boundary (9), static (9, including
/db/NMAS), stage (4), and moving (4, AASHTO LRFD fixtures). No
regressions from the 2026-08-07 baseline. Reconfirmation, not a new
finding — same conclusion as 08-07, now current as of today's patch.
2026-08-13 (last) — Gen NX full verification pass, mirroring today's Civil NX session¶
User asked to do the same broad pass on Gen NX that Civil NX got earlier
today, "in a safe order." Plan: read-only sweep first, then the two
untested read-shaped POSTs, then the riskier /doc/* file-management
family (paths resolve on the NX host, /doc/SAVEAS has a known silent-
failure landmine), and the destructive full CRUD suite last.
Coverage-gap check (read-only, coverage.json only): of 363 gen-
applicable endpoints, 0 had zero live evidence, but 12 had never been
tested on Gen specifically (Civil-only so far): the whole /doc/*
file-management family (OPEN/CLOSE/SAVE/SAVEAS/STAGAS/
IMPORT/IMPORTMXT/EXPORT/EXPORTMXT), /post/PM, /post/
STEELCODECHECK, and the already-known-open /ope/GSBG.
Full read-only sweep, Gen NX v2.1 build 08/12/2026: 266/266 GET- capable resources answered, zero 404s — no regressions from prior sweeps.
/post/PM and /post/STEELCODECHECK, against the small analyzed
2-story concrete column (no RC design code configured): both answered
the same clean precondition errors/empty responses Civil got on 2026-
07-31 ("Please Check RC Design Code" for PM, {"message": ""} for
STEELCODECHECK). Route/shape confirmed on Gen for the first time.
/doc/* file family, path built from verify_connection()['user']
per save_as()'s own documented pattern (C:/Users/sjj0507/Documents/
gsdk_test.mgbx), all against the same disposable test model:
SAVEAS→OPEN(verify) →SAVE→CLOSE→OPEN(reopen): all clean, node count (3) intact through every step. First Gen confirmation for all four endpoints.EXPORT(JSON) succeeded; theIMPORTround trip on that same file failed —"MAINREBAR_B_FY must be > 0."— a clean, informative error (not a crash), but a genuine new finding: this model's exported JSON apparently carries an invalid rebar default that import-side validation rejects. Not root-caused (SDK export gap vs. a real product default-value bug is still an open question). Node count unaffected by the failed import.EXPORTMXTsucceeded; theIMPORTMXTround trip succeeded too, but with a warning that turned out to be inaccurate:"[Warning] Static Seismic/Wind Loads Data for User Type are deleted due to changes in Story Data."A follow-upGETon/db/SWIND,/db/SSEIS, and/db/STORshowed all three completely unchanged — nothing was actually deleted. Don't take this warning's wording as ground truth for final state; verify with GET, same lesson as the "a 200 doesn't mean success" family of findings, just inverted (a warning that doesn't mean failure either).STAGASnot tested — needs a construction-stage model, which this session's throwaway column doesn't have. Still open on Gen.
Session stayed "connected" and responsive throughout every step, no
crash, no hang. docs/coverage.json updated for /post/PM,
/post/STEELCODECHECK, and 8 of the 9 /doc/* endpoints (all but
STAGAS) with today's Gen findings; ROADMAP.md regenerated.
Last step: full scripts/live_crud_check.py --product gen run,
against the same patch build (v2.1, 08/12/2026), no --include-crashers
needed (nothing quarantined). 38/38 resources completed a full
create→read→update→read→delete→read round trip, zero failures — core
(10), props (7), boundary (9), static (9, including /db/NMAS), stage
(3 of 4; /db/CMCS is declared Civil-only in the checker by design).
The moving tier (4 cases) is also Civil-only in the checker, per the
2026-07-29 finding that /db/MVCD's Gen availability is per-CODE, not
unconditional — not re-litigated today. No regressions from any prior
Gen run. This closes out today's Gen NX pass at the same depth as the
Civil NX one earlier: read sweep, the 2 previously-Gen-untested
read-shaped POSTs, the /doc/* file family, and the full write suite,
all clean or with informative (non-crash) findings on this patch build.
2026-08-16 — new patch on both products: connection check only¶
User applied a new patch to both products and asked to confirm both
connect, with versions recorded. verify_connection() against fresh
.env MAPI keys (last-occurrence-wins for the duplicated
MIDAS_MAPI_KEY_GEN/MIDAS_MAPI_KEY_CIVIL lines) succeeded for both:
GEN: {'user': 'sjj0507@midasit.com', 'program': 'gen', 'connectionID': 'N-pAmlMjSg', 'keyVerified': True, 'status': 'connected'}
CIVIL: {'user': 'sjj0507@midasit.com', 'program': 'civil', 'connectionID': 'WjbfiHlPSw', 'keyVerified': True, 'status': 'connected'}
About-dialog screenshots confirm the new build:
- MIDAS GEN NX 2026 (v2.1), build 08/14/2026 (previous: 08/12/2026)
- MIDAS CIVIL NX 2026 (v2.2), build 08/14/2026 (previous: 08/12/2026)
Same version numbers as the prior patch, two-days-newer build only — consistent with a routine patch rather than a version bump.
2026-08-16 (last) — full live_crud_check.py reconfirmation on both products, build 08/14/2026: 43/43 Civil, 38/38 Gen, no regressions¶
User asked to verify the new 08/14/2026 patch after confirming both
products connect (see the connection-check entry above). Chose the full
CRUD-suite scope over a read-only sweep or a targeted re-test of past
crash items, and explicitly authorized the /doc/NEW data loss ("go
go") after being warned.
Ran scripts/live_crud_check.py with no --tier filter (all tiers) on
each product in turn, Civil first:
- Civil NX (v2.2, build 08/14/2026): 43/43 — identical result set to
the 08/12/2026 run (
core/props/boundary/static/stage/movingall pass, including/db/NMASclean via the auto-filledrmX/rmY/rmZworkaround). - Gen NX (v2.1, build 08/14/2026): 38/38 — same as 08/12/2026 (no
movingtier; those fixtures are Civil-confirmed only, per the tier's own docstring in the script).
No crashes, no hangs, no field/value regressions surfaced on either product. This is a clean re-confirmation, not new coverage — same cases as the 08-13 runs, one patch build later.
2026-08-16 (last) — expanding write coverage past the curated CRUD suite: batch 1 of db.project/db.boundary, 14/18 confirmed¶
User asked what to do next on the new 08/14/2026 patch. Given the existing
live_crud_check.py suite only covers a curated 43/38-endpoint subset while
174 /db/* endpoints across both products were still only read-verified,
picked expanding write coverage over re-chasing GSBG's open blocker or the
Gen MAINREBAR_B_FY import bug. User chose to start with db.project +
db.boundary's 24 read-only endpoints (of 174 total), scoped down to a
tractable 18 for this session — deferred the 5 seismic-device endpoints
(SDVI/SDVE/SDST/SDHY/SDIS, deeply nested COMMON payloads) and DRLS
(empty-object payload, no field to round-trip) to a future batch.
Added a new extras1 tier to scripts/live_crud_check.py (18 cases, 3 new
seed steps) covering: PJCF, STYP, STYP-M1, TDGR, NPLN, CO_M,
CO_S, CO_T, CO_F, SPAN, NLLP, NLNK, NLNK-M1, CGLP, PRLS,
MLFC, PZEF, CLDR. First live run (Civil NX v2.2, build 08/14/2026):
10/18, all failures fixture problems as expected on a first pass. Triaged
and fixed 4 of them:
/db/PJCF: a fresh document already carries a Project Info record at id 1 (non-empty placeholder, confirmed viaProjectInfo.items()before any case ran), and POST answers "Key Already Exist" for any id, not just 1, until that record is deleted first. Same singleton family as UNIT/STYP, just with DELETE as the unlock instead of being GET/PUT-only. Added apjcf_unlockseed step./db/STYP:MASS: 0answers "Wrong Field" — valid values are only1(Lumped) or2(Consistent) per the manual's own enum, which the first payload didn't respect. Fixed toMASS: 1./db/SPAN:SPAN_BASE_ITEMS.lengthmust beSPAN_LIST.length + 1(one support point per span boundary) — a 2-items/3-list mismatch answered"[Error] ... (Item:Number of Spans)". The manual's Specifications table doesn't state this relationship; only cross-checking against its own worked JSON example (4 items / 3 values) surfaced it. Fixed to 3 items / 2 values./db/CO_F: keyed by a Floor Load Type (/db/FBLD) id, which the base seed model doesn't create — needed its ownfbld_seedstep. Also found live that CO_F's own"NAME"field is read-only, mirroring the linked FBLD record's name: a PUT withNAME="FL_CRUD"echoed back"FL_SEED"unchanged. Switched the case to probe a colour field (WF_R) instead, matching CO_M/CO_S/CO_T.
One failure did not resolve to a fixture problem: /db/NLLP
(General Link Properties) answers a generic "Unknown Error" on POST even
with the manual's own request-example payload reproduced verbatim
(PROPERTY_NAME/DESC/APPLICATION_TYPE="ELEMENT"/
APPLICATION_TYPE_D="SPG"/TOTAL_WEIGHT/OPT_USE_MASS), tried against
both a partially-seeded and a completely fresh /doc/NEW document, on
both Civil NX and Gen NX. Left as level: read in coverage.json —
genuinely unresolved, not swept under a workaround. This blocks NLNK,
NLNK-M1, and CGLP (all reference an NLLP record by name), so those
three stayed untested this round too; their coverage.json entries got a
2026-08-16 note saying so rather than being silently left stale.
Final result after fixes, re-run fresh on both products:
- Civil NX (v2.2, build 08/14/2026): 14/18 (
STYP-M1/SPANare Civil-only and both pass;NLLP/NLNK/NLNK-M1/CGLPare the 4 failures/blocks). - Gen NX (v2.1, build 08/14/2026): 12/15 (
STYP-M1/SPANcorrectly skipped as Civil-only;NLLP/NLNK/CGLPare the 3 failures/blocks — sameNLLProot cause reproduces here too, ruling out a Civil-specific explanation).
All 14 passing cases flipped to confirmed=True in the script.
docs/coverage.json updated for all 14 to level: "write"; /db/NLLP,
/db/NLNK, /db/NLNK-M1, /db/CGLP stay level: "read" with a dated
note. ROADMAP.md regenerated. Batch 2 (the remaining ~150 read-only
/db/* endpoints, including the deferred seismic-device family and the
NLLP root cause) is open for a future session.
2026-08-16 (last) — write coverage batch 2: db.misc_loads in full + 3 of db.temperature_prestress, 11/12 confirmed¶
Continuing the write-coverage push from earlier today (batch 1: db.project
+ db.boundary, 14/18). User asked to keep going; picked db.misc_loads (all
9 of its endpoints were still read-only) plus 3 tractable
db.temperature_prestress endpoints (GTMP/STMP/BTMP — the other 7 are
tendon/prestress geometry, deferred alongside extras1's seismic-device
family).
Found a genuine shortcut: docs/manual/11_DB_Settlement_Misc_Loads.md ends
with an "End-to-End 워크플로우 예제" that chains all 8 core misc_loads
endpoints (SMPT → SMLC → PLCB → LDSQ → WVLD → IELC → EFCT → INMF) through
one consistent fixture. Adapted its payloads almost verbatim into a new
extras2 tier (12 cases, 1 seed step) instead of re-deriving fixtures from
the Specifications tables like extras1 needed to.
First live run (Civil NX v2.2, build 08/14/2026): 10/12. Triaged the 2 failures:
/db/SMPT: same renumbering behaviour as/db/STLD//db/FBLD— a seed record requested at id 90 landed at id 1 instead, colliding with the case's own id-1 target ("Key Already Exist"). Fixed by moving the case to id 2./db/WVLD: did not resolve to a fixture problem. The manual's own full canonical example, reproduced verbatim, answered"Wrong Field"— and so did a bare{"NAME": "..."}payload, ruling out any specific field. Left atlevel: read, same class of unexplained finding as extras1's/db/NLLP(possibly a licensed offshore/marine module gate, unconfirmed).
Re-run clean on both products after the fix:
- Civil NX (v2.2, build 08/14/2026): 11/12 (
PLCB/WVLDare Civil-only;WVLDis the one remaining failure). - Gen NX (v2.1, build 08/14/2026): 10/10 — clean pass, exit code 0.
PLCB/WVLDcorrectly skipped as Civil-only.
Incidental finding: /db/BTMP (Beam Section Temperature) passed live
on both products, despite the manual's own prose flagging it
"⚠️ MIDAS Civil NX 전용 기능" (Civil-NX-only) — its Specifications table
carries no such restriction and coverage.json already listed both products.
Same documented-vs-actual-routing mismatch pattern as the ch08/ch17
moving-load family from 2026-07-29.
11/12 cases flipped to confirmed=True; docs/coverage.json updated for
all 11 to level: "write", /db/WVLD stays level: "read" with a dated
note. ROADMAP.md regenerated. Batch 3 (properties.*, analysis_control,
dynamic_loads, design, moving_loads, construction_stage, load_combinations,
pushover, bridge, the tendon/prestress half of temperature_prestress, the
deferred seismic-device family, and root-causing NLLP/WVLD) is open for
a future session.
2026-08-16 (last) — write coverage batch 3: tractable subset of db.properties.*, 4-5/9 confirmed¶
Third write-coverage batch of the day. Picked 9 of db.properties.*'s
15 remaining read-only endpoints — deferred the 5 fiber/inelastic-hinge
endpoints (IMFM, EPMT, FIMP, IEHC, IEHG, needing real
stress-strain curve fixtures) and FIBR (depends on FIMP), same
complexity class as extras1's deferred seismic-device family.
First live run (Civil NX v2.2, build 08/14/2026): 5/9. Unlike batches 1-2, most of the 4 failures did not resolve to fixture problems even after reproducing each endpoint's own manual canonical example verbatim:
/db/GRDP(Group Damping): tried both a minimal Specifications-table payload and the manual's full worked example (withGROUP_NAMEas the material's numeric id"1", not its name — the actual fixture bug found — plus the extraSTIFF_COEF_DEFAULT/MASS_COEF_DEFAULT/OPT_*_PROP_DEFAULTfields the manual's example includes but its own Specifications table omits). Both answered"Wrong Field"identically./db/TDMF(Time Dependent Material – User Defined): reproduced the manual's own 4-pointvDAYRequest Body example verbatim (not just the 2-point one first tried) — still"Wrong Field"./db/RPSC(Section Manager – Reinforcements): manual's own example reproduced — still fails./db/STRPSSM(Section Manager – Stress Points, Civil-only): manual's own example reproduced — still fails. Noteworthy: RPSC's own worked example keys at id401and STRPSSM's at id9003, not a plain running number like every other endpoint tested today — both describe "PSC/RC 단면" specifically, so the base seed's plainDBUSERrectangular column section may not carry a Section Manager reinforcement/stress-point slot at all. Left genuinely unresolved rather than guessed at further.
All 4 are the same class of finding as /db/NLLP (extras1) and /db/WVLD
(extras2) — a manual-exact payload still answering a generic error live.
Three unrelated-looking endpoints failing identically on the first try of
a brand-new tier is unusual enough (vs. extras1/2's near-clean first
passes) that it's worth flagging as a pattern to watch, not just three
independent coincidences — worth revisiting with a fresh angle rather than
more payload guessing.
Re-run clean on both products (no changes needed for the passing 5):
- Civil NX (v2.2, build 08/14/2026): 5/9 (
EDMP,PSSF,VSEC,VBEM,EWSFpass;GRDP/TDMF/RPSC/STRPSSMare the 4 failures). - Gen NX (v2.1, build 08/14/2026): 4/7 (
STRPSSM/EWSFcorrectly skipped as Civil-only;GRDP/TDMF/RPSCreproduce the identical failure, ruling out a Civil-specific explanation for those three).
5 passing cases flipped to confirmed=True; docs/coverage.json updated
for EDMP/PSSF/VSEC/VBEM/EWSF to level: "write".
GRDP/TDMF/RPSC/STRPSSM stay level: "read" with dated notes.
ROADMAP.md regenerated. Batch 4 (the deferred fiber/inelastic-hinge
family, analysis_control, dynamic_loads, design, moving_loads,
construction_stage, load_combinations, pushover, bridge, the
tendon/prestress half of temperature_prestress, and root-causing
NLLP/WVLD/GRDP/TDMF/RPSC/STRPSSM) is open for a future session.
2026-08-16 (last) — write coverage batch 4: db.load_combinations in full, 7-8/8 confirmed¶
Fourth write-coverage batch of the day. db.load_combinations was a clean
target: all 8 of its endpoints were still read-only, and 6 of them
(LCOM-GEN/CONC/STEEL/SRC/STLCOMP/SEISMIC) share one payload shape per
the module's own LoadCombinationPayload docstring, so the tier is mostly
one parametrized case repeated six times.
First live run (Civil NX v2.2, build 08/14/2026): 7/8 — only
LCOM-SEISMIC failed, with "The Load Combination Type is not
supported" for the same ANAL="ST" payload its 5 siblings all accept.
The interesting part: re-running on Gen NX passed 8/8, including
LCOM-SEISMIC with the identical payload. This is the first
same-day, same-build, same-payload case in today's batches where the two
products actually disagree rather than one being a documented
Gen/Civil-only route — Civil genuinely rejects ANAL="ST" for seismic
combinations while Gen accepts it. Tried the manual's own ANAL="RS"/
"CS" alternative on Civil against the base seed's plain static "DL"
case; both answered "Unknown Error" instead, because DL isn't
actually an RS/CS-typed case — reproducing the manual's intended usage
properly needs a real /db/SPLC Response Spectrum Load Case, which is
one of the db.dynamic_loads endpoints deferred to a future batch. Left
LCOM-SEISMIC at level: read rather than call it "confirmed" off an
asymmetric pass.
7 cases (LCOM-GEN, LCOM-CONC, LCOM-STEEL, LCOM-SRC,
LCOM-STLCOMP, CUTL, CLWP) flipped to confirmed=True;
docs/coverage.json updated for all 7 to level: "write".
LCOM-SEISMIC stays level: "read" with a dated note explaining the
product asymmetry. ROADMAP.md regenerated.
Running total for today's write-coverage push (batches 1-4): 42 endpoints
flipped to level: write, 7 left as genuinely unresolved live findings
(NLLP, WVLD, GRDP, TDMF, RPSC, STRPSSM, LCOM-SEISMIC). Batch
5 (the deferred fiber/inelastic-hinge family, db.analysis_control,
db.dynamic_loads — which would also unblock LCOM-SEISMIC —
db.design, db.moving_loads, db.construction_stage's hydration
family, db.pushover, db.bridge, and the tendon/prestress half of
db.temperature_prestress) is open for a future session.
2026-08-16 (last) — write coverage batch 5: db.dynamic_loads, new Gen NX crash found and filed as MAPI-2468¶
Fifth write-coverage batch of the day, and the one this session picked
specifically because it also unblocks batch 4's LCOM-SEISMIC finding
(needs a real /db/SPLC Response Spectrum Load Case). Covered 9 of
db.dynamic_loads's 12 endpoints — deferred the 3 Hyper-S variants
(THGC-M1/THOO-M1/THIS-M1, deeply-nested required control
sub-objects).
First live run (Civil NX v2.2, build 08/14/2026): 4/9. Triaged and fixed 3 fixture issues:
/db/SPFC//db/THIS//db/THFC: all three answered "Key Already Exist" — their seed records (requested at id 90) renumber to id 1, same STLD/FBLD/SMPT family, so the cases' own test records collided at the same id. Fixed by moving the cases to the next free id (2, or 3 where/db/THFChas two seed records ahead of it)./db/THNL: "Unknown Error" — itsFUNC_NAMEmust reference a Force/Moment-type function (iTYPE=3/4), not the Accel-type one (iTYPE=2) already seeded for other cases; added a second, Force-typed seed function. This matches the manual's own explicit warning ("FUNC_NAME에는 Force 또는 Moment 타입의 시간이력 함수만 사용 가능"), just missed on the first pass.
Re-run clean on Civil NX after the fixes: 8/9 (only THMS still fails —
see below).
Then, running the same tier on Gen NX, the product crashed mid-run.
SPLC failed normally ("Unknown Error", session still alive), THGC/
THIS/THFC/THGA all passed clean, then THNL's own case showed
create=ok read_back=ok update=FAIL — its PUT failed with 404 Client
Disconnected, and the very next case (THSL) got 404 client does not
exist. The checker's own _session_lost guard caught it and aborted
correctly. User asked directly ("Gen NX 뭐가 죽은 것 같은데?") mid-run;
confirmed via a direct verify_connection() call that status itself
had flipped to "disconnected" — not just a blocked-dialog false
"connected" (the documented verify_connection()-can't-see-a-blocked-
session landmine). This was a genuine session death, not a masked hang.
User restarted Gen NX, then explicitly asked to reproduce the same
scenario again ("다시 한번 같은 상황 연출해봐"). Re-ran the identical
tier on the freshly-restarted Gen NX session and the crash reproduced at
the exact same point: SPLC "Unknown Error" (alive), four clean
passes, THNL create=ok read_back=ok update=FAIL with the same 404
Client Disconnected, then the aborted follow-on call. Two independent
reproductions, same trigger, same build (v2.1, 08/14/2026) — this
project's usual "confirmed" bar.
Isolated the trigger precisely: POST /db/THNL succeeds and the
following GET echoes it back correctly, but the next PUT
/db/THNL ({"Assign": {"1": {"ITEMS": [{...}]}}}, only
SCALE_FACTOR changed from the create payload) is what kills the
session — not the POST itself. Civil NX's identical round trip is
completely clean.
User asked to write this up and file it in MIDASIT's internal Jira
("내용 정리하고 Jira에 올리자 이건"). Searched project = MAPI AND (text
~ "THNL" OR text ~ "Dynamic Nodal Load") first — no existing ticket for
this symptom (the only THNL hit, MAPI-177, is an unrelated closed 2023
delete-behavior ticket). Filed MAPI-2468, parented under MAPI-2427
("API 동작 중 프로그램 종료 이슈" — the same crash-symptom epic that
already holds MAPI-2378/NMAS, MAPI-2425/EDMP, MAPI-2426/USLC, and
MAPI-2431/CD-TABLE), priority 높음 (id: "3", matching all four
precedents), with the exact reproduction payload, both reproduction
timestamps, and an open "root cause not yet identified" framing — not
linked to the other four as a shared cause, since there's no evidence of
one, same call this project made for MAPI-2431.
live_crud_check.py's /db/THNL case is now restricted to
products=("civil",) rather than fully crash-quarantined, since only
Gen NX actually crashes and Civil's round trip is clean — re-enabling Gen
needs a deliberate decision once MAPI-2468 is resolved, not an assumption
that a vendor fix landed.
One more genuinely unresolved finding, same class as extras1/2/3's
NLLP/WVLD/GRDP/TDMF/RPSC/STRPSSM: /db/THMS answers
"Wrong Field" on both products even with the manual's own field names
and a required-fields-only payload. db/dynamic_loads.py's own docstring
already flags it as "Keyed by node/group id" (unlike THNL's plain node
id) — may need a real multi-support boundary/support group defined
first, not just any integer key.
Also product-asymmetric (mirroring LCOM-SEISMIC's reversed case from
batch 4): /db/SPLC passes clean on Civil NX but Gen NX answers
"Unknown Error" on the identical payload, reproduced twice — left at
level: read pending investigation (possibly needs a Gen-specific modal/
eigenvalue analysis-control setup).
Final tally: 6 endpoints (SPFC, THGC, THIS, THFC, THGA, THSL)
write-confirmed on both products; THNL write-confirmed on Civil only
(Gen crashes, MAPI-2468 filed); SPLC and THMS stay level: read as
genuine unresolved findings. docs/coverage.json and ROADMAP.md
updated accordingly.
Running total for today's write-coverage push (batches 1-5): 49 endpoints
flipped to level: write, 9 left as genuinely unresolved live findings
(NLLP, WVLD, GRDP, TDMF, RPSC, STRPSSM, LCOM-SEISMIC,
SPLC, THMS), plus one new confirmed live crash (THNL PUT on Gen NX,
MAPI-2468). Batch 6 (the deferred fiber/inelastic-hinge family, seismic
devices, db.analysis_control, db.design, db.moving_loads's
remainder, db.construction_stage's hydration family, db.pushover,
db.bridge, the tendon/prestress half of db.temperature_prestress, the
3 deferred Hyper-S dynamic_loads variants, and root-causing the 9 unresolved
findings above) is open for a future session.
2026-08-16 (last) — write coverage batch 6: seismic-device family from db.boundary, 0/5 -- genuine finding, not a fixture bug¶
Sixth write-coverage batch of the day. Picked up the seismic-device family
(SDVI/SDVE/SDST/SDHY/SDIS) that batch 1 deliberately deferred out
of extras1 for its deeply nested COMMON payloads. Transcribed each
payload from the manual's own POST worked examples (05_DB_Boundary.md,
items 16-20), not the leaner Specifications tables, per this project's
standing preference for worked examples over tables when they disagree.
First live run (Civil NX v2.2, build 08/14/2026): 0/3 (SDHY/SDIS are
Gen-only, correctly skipped). All three POSTs answered "Wrong Field".
Bisected by hand rather than assuming a fixture bug outright, given this
project's own caution about varying values before field names:
- Reproduced the manual's own bare literal
SDVIexample verbatim (DAMPER_TYPE=0,DASHPOT_TYPE=0— Linear Elastic, not the customized=2Exponential values the case used) — still"Wrong Field". - Fetched
SDVI.info()'s live JSON schema: field names match the manual exactly (COMMON.{NAME,DESC,INPUT_METHOD,COMPANY,PRODUCT_NAME, TYPE_NUMBER},DEVICE_TYPE,DAMPER_TYPE,DASHPOT_TYPE,INPUT_TYPE,ITEM[6]— plus an undocumentedINPUT_TYPE_EXFNand per-itemEXFN_*fields for the Exponential dashpot type), ruling out a field-name typo. - Tried a
COMMON-only payload, a payload withINPUT_TYPE_EXFNadded, noITEMarray, noDEVICE_TYPE, non-emptyCOMPANY/PRODUCT_NAME/TYPE_NUMBEReverywhere,INPUT_METHOD=1(Reference DB), and a single-entryITEMarray instead of 6 — every variant still answered"Wrong Field". - Extended the same manual-literal-payload test to
SDVE/SDST(Civil) andSDHY/SDIS(Gen) — all 4 failed identically, and a full--tier extras6run on Gen NX confirmed all 5 fail there too (SDHY/SDISincluded this time, since Gen is where they're actually implemented).
Not resolved as a fixture problem on either product. Notably, /db/NLLP
(General Link Properties, extras1's confirmed-broken endpoint) is the
table these five devices are meant to be referenced from
(APPLICATION_TYPE_D="VI"/"VE"/"ST"/"HY"/"IS") — two independently
confirmed-broken endpoint families in the same manual chapter raises
suspicion of something chapter-wide (a licensed isolator/damper-design
module gate not enabled on this session?) rather than five unrelated
defects, but that's unconfirmed speculation, not a finding to act on.
Left all 5 at level: read in docs/coverage.json, each with a dated
note; none of the extras6 cases are confirmed=True since none have
ever passed live. /db/DRLS (#24, the other endpoint extras1 deferred out
of this chapter) stays deferred for an unrelated, purely mechanical reason:
its payload is {<node id>: {}}, an empty object with no field to
distinguish a create from an update, so it doesn't fit this checker's
create/update/probe shape at all — a different problem from the
"Wrong Field" finding above. ROADMAP.md regenerated (399/399
implemented, write-count unchanged by this batch since nothing passed).
Running total for today's write-coverage push (batches 1-6): still 49
endpoints flipped to level: write (batch 6 added 0), 14 left as
genuinely unresolved live findings (NLLP, WVLD, GRDP, TDMF, RPSC,
STRPSSM, LCOM-SEISMIC, SPLC, THMS, SDVI, SDVE, SDST, SDHY,
SDIS), plus the one confirmed live crash from batch 5 (THNL PUT on Gen
NX, MAPI-2468). Batch 7 (the deferred fiber/inelastic-hinge family,
db.analysis_control, db.design, db.moving_loads's remainder,
db.construction_stage's hydration family, db.pushover, db.bridge,
the tendon/prestress half of db.temperature_prestress, the 3 deferred
Hyper-S dynamic_loads variants, /db/DRLS's empty-payload shape problem,
and root-causing the 14 unresolved findings above) is open for a future
session.
2026-08-17 — write coverage batch 7: standalone/frame-attachable remainder of db.static_loads, 4/5 (Civil) + 5/8 (Gen)¶
Seventh write-coverage batch, continuing without a fresh scope check —
picked the remaining db.static_loads endpoints that don't need real
plate/planar geometry beyond what the base seed model already has: PNLD,
PNLA, FBLA, FMLD, POSP, EPST, POSL, EPSE. (PNLA was
initially assumed to need real plate geometry and deferrable, but
_seed_model's own base model already has one — element 4, nodes 5-8 — so
it came along for free.)
First live run (Civil NX v2.2, build 08/14/2026): 1/5, only FMLD passed
clean. Triaged the rest:
/db/PNLD: create succeeded but read-back failed — same STLD/FBLD-family renumbering as extras1/extras5's seeds (the seed landed at id 1, not the requested 90; the case's own id 91 request actually landed at id 2). Fixed by pointing the case at id 2 and citing the seed's real id./db/PNLA:"Wrong Field"even after fixingPNLD_KEYto the seed's real id. Bisected by removing one field at a time — the manual marksLOAD_GROUPRequired, but sending any non-empty value for it answers"Wrong Field"; omitting the key (or sending"") succeeds. Fixed by dropping it;db/static_loads.py'sPlaneLoadPayloaddocstring now documents this./db/POSL:"Wrong Field"on every payload, including the manual's own literal example. Fetched.info()'s live schema: Civil NX's actual field set isNAME/SZ/SRF/SC/FA/FV/DAMP_RATIOonly — a completely different, smaller shape than the manual'sCODE/METHOD/EPA/SDS/SD1/USER_GROUP/IF/RMF. SendingCODEat all — even as""— was confirmed as the specific trigger via per-field bisection. Checked Gen NX's own.info()schema for comparison and found it does match the manual (plus two undocumented fields,EPGAeff/Kae) — this endpoint's real contract is product-asymmetric, not just wrong. Split the checker's onePOSLcase into two, one payload per product;SeismicLoadParamPayloadindb/static_loads.pynow documents both shapes.
Re-run clean on Civil after the PNLD/PNLA/POSL fixes: 4/5
(PNLD, PNLA, FMLD, POSL pass; FBLA is the one remaining
failure, "Unknown Error" on POST, not resolved as a fixture problem
despite trying the base model's plate-corner nodes and an unrelated
frame-chain node set, both winding orders, FLOOR_DIST_TYPE 1 and 2, and
the manual's full optional-field set).
Gen NX (v2.1, build 08/14/2026), same fixed cases plus the Gen-only
trio (POSP/EPST/EPSE): 5/8. POSP's first attempt answered
"Unknown Error" with a single soil layer — fixed by using the manual's
own 3-layer example, whose HEIGHT values sum to exactly the depth from
GROUND_LEVEL to BEDROCK_LEVEL (5+5+15=25m); a single layer that
doesn't reach that depth appears to be the trigger, the same undocumented
cross-field relationship class as /db/SPAN's item-count-vs-list-length
rule from batch 1. POSP, PNLD, PNLA, FMLD, POSL all passed
clean after fixture fixes. EPST and EPSE both failed "Wrong Field"
even with field names confirmed matching their own live /info schema
and several bisected variants (SEL_TYPE, EP_TYPE, blank SOIL_PROP,
omitted IN_PT, PRES_PROFILE_ITEMS) — not resolved as fixture
problems, left level: read alongside FBLA. EPST/EPSE are untested
on Civil NX since POSP (which their SOIL_PROP references) is
Gen-only, so there's no Civil fixture to build them against — genuinely
untested there, not confirmed broken.
6 endpoints flipped to level: write (PNLD, PNLA, FMLD, POSP,
POSL — POSL write-confirmed on both products despite the schema
split); FBLA, EPST, EPSE stay level: read as new unresolved
findings. docs/coverage.json and ROADMAP.md updated; pytest (701)
and ruff both clean after the source-level PlaneLoadPayload/
SeismicLoadParamPayload docstring fixes.
Running total for the write-coverage push (batches 1-7): 55 endpoints
flipped to level: write, 17 left as genuinely unresolved live findings
(NLLP, WVLD, GRDP, TDMF, RPSC, STRPSSM, LCOM-SEISMIC,
SPLC, THMS, SDVI, SDVE, SDST, SDHY, SDIS, FBLA, EPST,
EPSE), plus the one confirmed live crash from batch 5 (THNL PUT on
Gen NX, MAPI-2468). Batch 8 (the deferred fiber/inelastic-hinge family,
db.node_element's Domain feature, db.analysis_control, db.design,
db.moving_loads's remainder, db.construction_stage's hydration
family, db.pushover, db.bridge, the tendon/prestress half of
db.temperature_prestress, the 3 deferred Hyper-S dynamic_loads
variants, /db/DRLS's empty-payload shape problem, and root-causing the
17 unresolved findings above) is open for a future session.
2026-08-17 — write coverage batch 8: tractable subset of db.analysis_control, 5/9 (Civil) + 7/9 (Gen)¶
Eighth write-coverage batch, continuing without a fresh scope check.
Picked 9 of db.analysis_control's 21 endpoints: the singleton "control
data" tables (ACTL, PDEL, BUCK, EIGV, HHCT, MVCT, SMCT,
NLCT, BCCT) — each is one record at id 1, referencing only the base
seed model's own load cases, so no per-tier geometry was needed. Deferred
the 5 Hyper-S (-M1) variants and the 4 country-specific MVCT
variants (MVCTch/id/bs/tr) as before.
First live run (Civil NX v2.2, build 08/14/2026): 3/9. Triaged:
/db/EIGV: the manual's own first worked example usesTYPE: "EIGEN"(Subspace Iteration) and answered"FREQ_RANGE is required for LANCZOS."— the server doesn't recognize"EIGEN"as a validTYPEat all despite the manual and/info's own schema both listing it; only"LANCZOS"/"RITZ"work. Fixed by switching to the manual's own Lanczos example./db/BCCT:vBOUNDARY'svBGentries ("BG1"/"BG2", the manual's own example values) answered"Boundary Group not found: BG1"— they have to be real/db/BNGRboundary groups, not free-text labels. Fixed with abngr_seedstep.
Three did not resolve to fixture problems, each a distinct kind of finding:
/db/ACTL:TOLnever persists as written on Civil NX — POST succeeds and reads back correctly, but a follow-up PUT changing onlyTOLreads back the original value every time, confirmed even after a full DELETE + fresh POST with a differentTOL. On Gen NX the same payload fails outright with"Wrong Field", including a bare{ITER, TOL}-only payload — and Gen's own/infoschema turns out to differ from Civil's/the manual's (noCLATS, an undocumentedACWCthat answers"Wrong Key"when sent, ruling that field out too as the cause). Two different symptoms, same verdict: unresolved on both./db/NLCT: Civil NX answers"LINE_SEARCH_OPTION is required when OPT_ENABLE_LINE_SEARCH is true."— neither field is in the manual or in/info's own live schema. TriedOPT_ENABLE_LINE_SEARCH: falseexplicitly (same error) and a guessedLINE_SEARCH_OPTION: "AUTO"alongsidetrue(also unchanged). Passes clean on Gen NX with the exact same payload — product-asymmetric, so the checker now carries two cases, one per product./db/HHCT: Civil NX'sTHETAfield has the same "write doesn't stick" symptom asACTL'sTOL— POST succeeds with no error body, but the immediate GET readsTHETAback as0, not the1sent, reproduced twice. Also passes clean on Gen NX with the identical payload — another product-asymmetric split into two cases./db/MVCT: fails on both products, with different errors depending on payload completeness — the manual's full example answers"Unknown Error"on both Civil and Gen, a stripped-down minimal payload answers"Wrong Field"on Civil instead. Field names match/infoon both. Noted as possibly needing the full AASHTO LRFD moving-load fixture chain (code → lane → vehicle → case, see the"moving"tier) built first rather than being a standalone control table like its 8 siblings here — untested, not pulled into this tier's scope given the size of that prerequisite chain.
Final result after fixes, re-run clean on both products:
- Civil NX: 5/9 (
PDEL,BUCK,EIGV,SMCT,BCCTpass;ACTL,HHCT,MVCT,NLCTare the 4 failures). - Gen NX: 7/9 (same 5 plus
HHCTandNLCT;ACTLandMVCTare the 2 failures, confirmed as genuinely both-product issues rather than Civil-specific).
9 endpoints flipped to level: write total — 5 confirmed both products
(PDEL, BUCK, EIGV, SMCT, BCCT), 2 confirmed Gen-only (HHCT,
NLCT, with the level: write reflecting the Gen NX result specifically
per this project's convention of documenting the asymmetry rather than
averaging it away). ACTL and MVCT stay level: read as new
unresolved findings. docs/coverage.json and ROADMAP.md updated;
pytest (701) and ruff both clean.
Running total for the write-coverage push (batches 1-8): 64 endpoints
flipped to level: write, 19 left as genuinely unresolved live findings
(NLLP, WVLD, GRDP, TDMF, RPSC, STRPSSM, LCOM-SEISMIC,
SPLC, THMS, SDVI, SDVE, SDST, SDHY, SDIS, FBLA, EPST,
EPSE, ACTL, MVCT), plus the one confirmed live crash from batch 5
(THNL PUT on Gen NX, MAPI-2468). Batch 9 (the deferred fiber/
inelastic-hinge family, db.node_element's Domain feature, the
remaining 12 of db.analysis_control — 5 Hyper-S + 4 country-specific
MVCT + MVCT itself once its moving-load prerequisite chain is worked
out, db.design, db.moving_loads's remainder, db.construction_stage's
hydration family, db.pushover, db.bridge, the tendon/prestress half
of db.temperature_prestress, the 3 deferred Hyper-S dynamic_loads
variants, /db/DRLS's empty-payload shape problem, and root-causing the
19 unresolved findings above) is open for a future session.
2026-08-17 — write coverage batch 9: db.node_element's Domain feature, 0/3 -- /db/MADO silently drops writes¶
Ninth write-coverage batch. Picked up db.node_element's last 3
read-only endpoints: MADO, SBDO, DOEL (the "Domain" feature used
for 2D/plane-stress mesh assignment). Small, self-contained scope —
SBDO/DOEL both reference a MADO domain by name/id, so the tier
needed a mado_seed creating two named domains (DM1_SEED/DM2_SEED)
plus DOEL's own case targeting the base seed model's existing plate
element (id 4, nodes 5-8) rather than building new geometry.
First live run (Civil NX v2.2, build 08/14/2026): 0/3, all three
read_back failures with "id N missing after wrote". Initially looked
like the familiar STLD/FBLD-family renumbering gotcha from earlier
batches, but a manual GET on all three tables immediately after
create() came back completely empty — not renumbered, genuinely never
written. Bisected /db/MADO specifically since it's the root of the
dependency chain:
POST /db/MADOanswers{"message": ""}— HTTP success, no error body at all — but the record is absent from a follow-upGET, every time.- Reproduced with several payload variants:
MATL/PROPpointed at the base model's real material/section ids (1/1),SUB_TYPE1 and 2,TYPE3 (Plane Stress) instead of 4 (Plate), and — the clearest signal — the manual's own literal request-body example reproduced verbatim (NAME: "DM1",MATL: 0,PROP: 0,SUB_TYPE: 2). All silently no-ops. - Checked Gen NX independently: identical
{"message": ""}/ empty-GET result with the same literal manual payload.
Not resolved as a fixture problem on either product — a "message": ""
success response that doesn't persist anything is a different failure
shape than this session's usual "Wrong Field"/"Unknown Error" class,
closer to the /doc/SAVEAS "command complete but no file written"
pattern this project has already documented elsewhere, just on a /db/*
table instead of a file write.
SBDO and DOEL both fail as a direct consequence — they reference
DM1_SEED/DM2_SEED by name/id, and those domains were never actually
created — so their own field contracts remain genuinely untested, not
confirmed broken independently of MADO. Left all 3 at level: read
with dated notes making that dependency explicit; the checker's own
cases are kept (not skipped) so a future MADO fix can be verified to
unblock SBDO/DOEL without touching this tier's code again.
Caught and fixed one process mistake before finalizing: MADO's own
case was initially marked confirmed=True while drafting the fixture,
before it had actually been run live — the first real run correctly
flagged it as a regression (a confirmed case failing) rather than an
unverified failure, which would have been a false alarm. Reverted the
premature flag before this file/coverage.json were touched, so nothing
downstream saw the false regression.
docs/coverage.json and ROADMAP.md updated (all 3 stay level: read
— this batch added 0 to the write count, a clean miss rather than a
partial one). pytest (701) and ruff both clean.
Running total for the write-coverage push (batches 1-9): still 64
endpoints at level: write (batch 9 added 0), 22 left as genuinely
unresolved live findings (NLLP, WVLD, GRDP, TDMF, RPSC,
STRPSSM, LCOM-SEISMIC, SPLC, THMS, SDVI, SDVE, SDST,
SDHY, SDIS, FBLA, EPST, EPSE, ACTL, MVCT, MADO, SBDO,
DOEL), plus the one confirmed live crash from batch 5 (THNL PUT on
Gen NX, MAPI-2468). Batch 10 (the deferred fiber/inelastic-hinge
family, the remaining 12 of db.analysis_control, db.design,
db.moving_loads's remainder, db.construction_stage's hydration
family, db.pushover, db.bridge, the tendon/prestress half of
db.temperature_prestress, the 3 deferred Hyper-S dynamic_loads
variants, /db/DRLS's empty-payload shape problem, and root-causing the
22 unresolved findings above) is open for a future session.
2026-08-17 — write coverage batch 10: standalone subset of db.construction_stage's heat-of-hydration family, 5/7 both products¶
Tenth write-coverage batch. Picked 7 of db.construction_stage's 10
remaining read-only endpoints — the heat-of-hydration family
(ETFC/CCFC/HSFC/HAHS/HPCE/STBK/HSTG), deferring HECB and
HSPT (both keyed by a construction-stage id per the manual's own note)
and CSCS (references a stage name via ASTAGE) since none of the
three have a construction stage to attach to in this tier's fixture.
The chapter's own End-to-End workflow example covers 6 of these 7
(ETFC→CCFC→HSFC→HAHS→HECB→HSTG), so most payloads were
adapted from it rather than re-derived from the Specifications tables.
First live run (Civil NX v2.2, build 08/14/2026): 4/7. Triaged:
/db/HSFC:"Key Already Exist"— the case's own id (91) collided withhsfc_seed's second record, which the seed also placed at 91. Fixed by moving the case to id 92./db/HSTG: passed on the first try — the fixture used real/db/GRUP-family structure/boundary group names (SG10_SEED/BG10_SEED) from the start rather than the manual's own made-up group-name example, applying the/db/BCCTlesson from batch 8 proactively instead of hitting the same wall twice.
Two did not resolve to fixture problems:
/db/HAHS: answered"[Error] The element no. 1 is an element type in which Heat Source Assignment cannot be entered."for element 1 (a frame/beam). Switched to element 4 (the base model'sPLATE) — same error, just renumbered to element 4. Heat-of-hydration is normally applied to mass concrete (piers, footings) modelled asSOLIDelements, which the base seed model doesn't build — most likely a scope gap in this fixture rather than a confirmed product defect, since a realSOLIDelement was never tried. Left unconfirmed rather than treated as a genuine finding, pending that test in a future session./db/HPCE: answered"Wrong Key"— a different error class than this session's usual"Wrong Field"/"Unknown Error". Bisected field by field: every field exceptITEMSsucceeds individually (each answering the ordinary"Wrong Field"for an incomplete payload), but addingITEMS— with real frame node ids, real plate node ids, or a single node — always flips the error to"Wrong Key"instead. Live/info's own schema confirmsITEMSis a plain integer array, matching exactly what was sent, so this isn't a documented-vs-actual mismatch either. Also found two undocumented fields via/info(START_STAGE/END_STAGE, both strings, absent from the manual entirely) and tried them both empty and omitted — no change either way. Genuinely unresolved, same class as/db/FBLA//db/EPSTetc.
Re-run clean on both products after the HSFC/HSTG fixes:
- Civil NX: 5/7 (
ETFC,CCFC,HSFC,STBK,HSTGpass;HAHSandHPCEare the 2 failures). - Gen NX: same 5/7 split, confirming
HAHS/HPCEaren't Civil-specific.
5 endpoints flipped to level: write; HAHS and HPCE stay
level: read with dated notes. docs/coverage.json and ROADMAP.md
updated; pytest (701) and ruff both clean.
Running total for the write-coverage push (batches 1-10): 69 endpoints
flipped to level: write, 24 left as genuinely unresolved or
scope-limited live findings (NLLP, WVLD, GRDP, TDMF, RPSC,
STRPSSM, LCOM-SEISMIC, SPLC, THMS, SDVI, SDVE, SDST,
SDHY, SDIS, FBLA, EPST, EPSE, ACTL, MVCT, MADO, SBDO,
DOEL, HAHS, HPCE), plus the one confirmed live crash from batch 5
(THNL PUT on Gen NX, MAPI-2468). Batch 11 (the deferred fiber/
inelastic-hinge family, the remaining 12 of db.analysis_control,
db.design, db.moving_loads's remainder, db.construction_stage's
HECB/HSPT/CSCS — needs a real construction stage built first —
db.pushover, db.bridge, the tendon/prestress half of
db.temperature_prestress, the 3 deferred Hyper-S dynamic_loads
variants, /db/DRLS's empty-payload shape problem, retesting /db/HAHS
against a real SOLID element, and root-causing the 24 unresolved
findings above) is open for a future session.
2026-08-17 — correction: EIGV/BCCT were never actually confirmed on Civil NX¶
Re-reading batch 8's own raw result files (extras8_civil.json, checked_at
2026-08-16T15:47:37Z) while resuming the write-coverage push turned up a
mismatch: the code and docs/coverage.json both claimed /db/EIGV and
/db/BCCT were "confirmed both products," but that Civil NX run — the only
one ever made for these two cases — has both failing:
EIGV:"FREQ_RANGE is required for LANCZOS.", the identical error the batch-8 write-up already documents for the rejectedTYPE="EIGEN"attempt — except this run used the fixedTYPE="LANCZOS"payload (iFREQ/bMINMAX/FRMIN/FRMAX/bSTRUM) that passes clean on Gen. Civil answers the same error anyway, meaning it wants some additional, undocumented field literally namedFREQ_RANGEthatFRMIN/FRMAXdon't satisfy.BCCT:"Boundary Group not found: BG1"— the exact error thebngr_seedfix (creating real/db/BNGRgroups namedBG1/BG2) was supposed to clear, and did clear on Gen (extras8_gen.json, same day, 5 minutes later, both casesok). Civil rejects the same real groups under the same names.
No Civil retry for either case exists anywhere in this session's scratch
output (checked — extras8_civil2.json was never created, unlike
extras7_civil2.json/extras10_civil2.json which do exist for their
tiers' fixed re-runs). The "confirmed both products" claim was written
without actually re-running Civil after the Gen fix landed — caught this
time by cross-checking the code against its own raw evidence, per this
project's standing "verify before confirming already done" rule.
Fixed: both Cases split into products=("civil",) unconfirmed /
products=("gen",) confirmed=True, same pattern as HHCT/NLCT.
docs/coverage.json's method notes for both rewritten to state
"confirmed live on Gen NX; Civil NX fails" instead of "both products."
ROADMAP.md regenerated (399/399 implemented, unchanged — both stay
level: write since Gen alone still counts, same as HHCT/NLCT; the
batch 1-10 running total of 69 write / 24 unresolved is unaffected, only
the per-endpoint accuracy). pytest and ruff both clean.
Lesson for future batches: a product-split Case pair should be written
(and confirmed set) only after both products have actually been run
with the final payload — not after fixing one product's error and
assuming the fix generalizes.
2026-08-17 — batch 11a: /db/STCT (last tractable gap in db.analysis_control)¶
Of the 12 db.analysis_control endpoints left unattempted after batch 8, 11
are Hyper-S/-M1 variants or the four large country-specific MVCT
siblings (MVCTch/MVCTid/MVCTbs/MVCTtr) — all deferred as before.
/db/STCT (Construction Stage Analysis Control Data) was the one genuinely
tractable endpoint left, so it got its own tier (extras11) rather than
waiting for a bigger batch.
Needs a real construction stage to reference (FINAL_STAGE), so it reuses
the stage tier's own stage_1 seed (CS_SEED) — confirmed the manual's
own worked example makes the same mistake batch 8 already found on BCCT
(a made-up stage name, "CS1"), swapped for the real one from the start.
First Civil run failed immediately: POST /db/STCT answered "Key Already
Exist". Investigated directly (bypassing the harness) and found the real
shape of the problem — once a real construction stage is registered,
/db/STCT already holds an auto-populated default record at id 1,
before any explicit POST. Tried DELETE-then-POST to clear it first;
DELETE answered its own separate error: "[Error] Construction Stage
Analysis Control Data cannot be deleted when the Construction Stage is
registered." So this table is POST/DELETE-locked once construction
staging is in use — a real business rule, not a fixture bug.
Fell back to a manual PUT on the existing id-1 record instead. The PUT
itself succeeded and its response echoed iITER/TOL back correctly, but
a follow-up GET dropped both fields entirely (present in every other
respect — FINAL_STAGE, CPFC, bCONV/bTRUSS/bBEAM, bCAMBER,
bCHANGE_CABLE, iNLA_TYPE, bINC_TDE, bCNS, TYPE, iITER_CR,
TOL_CR all persisted). Repeated with a second iITER value to rule out
a one-off — same result both times.
Gen NX doesn't pre-populate a default record, so POST itself succeeds
there, but the identical iITER/TOL silent-drop reproduces on the very
first GET after create ("wrote 30, read back None") — confirming the
underlying defect is symmetric across both products, only the
POST-vs-already-exists wrinkle is Civil-specific. Likely cause: the
payload combines iINC_NLA=0 (Linear) with iNLA_TYPE=1 (Accumulative)
per the manual's own example, and the server may treat the two
Linear-only fields as inapplicable under Accumulative mode without saying
so.
Left as a genuine, product-symmetric finding — the Case in extras11
stays unconfirmed, documenting the Civil "Key Already Exist" failure
directly (closest to what a real caller hits first). docs/coverage.json
records the fuller story (both products, both failure modes) since the
generic harness can only exercise one of them per run. level stays
read. ROADMAP.md regenerated (399/399 implemented, unchanged);
pytest (701) and ruff both clean.
Running total for the write-coverage push (batches 1-11a): still 69
endpoints at level: write; 25 now carry a dated genuinely-unresolved or
scope-limited finding (the 24 from batches 1-10 plus STCT), plus the one
confirmed live crash (THNL PUT on Gen NX, MAPI-2468).
2026-08-17 — batch 11b: moving tier confirmed on Gen NX¶
The moving tier's own header comment had been sitting there since
2026-07-30 with a note that it should pass on Gen unchanged — the AASHTO
LRFD codes are the ones confirmed not gated by the region-code lock, and
the same fixture already round-trips clean on Civil — but that nobody had
actually run it against a Gen session. Ran it: core (base model) +
moving against Gen NX, identical fixture (MVCD "AASHTO LRFD", LLAN
line lanes, MVHL/MVHC HL-93 truck/tandem vehicles, MVLD vehicle
classes) — all 4 pass clean, full create→read→update→read→delete→read.
Re-ran Civil NX afterward too, to make sure widening products didn't
regress anything — also clean, 14/14 both times.
Widened all 4 Cases from products=("civil",) to both, kept
confirmed=True (already true from the Civil confirmation).
docs/coverage.json's method note for LLAN/MVHL/MVHC/MVLD
updated to record the Gen write-round-trip explicitly — they'd been
level: write already off the Civil result plus a separate GET-only Gen
sweep from 2026-08-10, so the write/read tally is unchanged; this closes
the Gen side from "route exists" to "write proven." ROADMAP.md
regenerated (399/399, unchanged); pytest (701) and ruff both clean.
2026-08-17 — batch 11c: /db/HECB//db/HSPT (construction-stage-keyed heat-of-hydration pair)¶
Batch 10 deferred HECB/HSPT/CSCS because they need a real construction
stage id, which this session's stage tier now provides (stage_1 seeds
CS_SEED at id 1). Added both to the extras11 tier alongside STCT.
Both are keyed by construction stage number in the Assign dict, not an
element or node id — the manual says so explicitly for each ("Assign의
키(ID)는 시공단계 번호입니다") — and both worked examples use made-up
group/function names ("BG_SURF", "CC_Standard", "AT_Summer" for
HECB; "BG_BASE" for HSPT), same mistake class as /db/BCCT's
"BG1"/"BG2". New hecb_seed creates real /db/BNGR, /db/CCFC, and
/db/ETFC records up front instead.
HSPT(Prescribed Temperature): passes clean on both products, first try.confirmed=True.HECB(Element Convection Boundary): fails on both products with"[Error] The element no. 1 is an element type in which Element Convection Boundary cannot be entered."— the exact same failure shape as/db/HAHSfrom batch 10.ITEMS[0].ID(documented as a plain serial number) lines up numerically with "element no. 1" in the error, so it's plausibly read as an element reference server-side despite the manual's description. GivenHAHShit the identical wall and heat-of-hydration boundary conditions are normally applied to mass-concreteSOLIDelements (piers, footings), this reads as the same fixture scope gap — the base model only builds frame and plate elements, noSOLID— rather than a fresh defect. Left unconfirmed, not re-bisected past that point.
1 endpoint (HSPT) flipped to level: write; HECB stays level: read
with a dated note pointing at the HAHS precedent. docs/coverage.json
and ROADMAP.md updated; pytest (701) and ruff both clean.
Running total for the write-coverage push (batches 1-11c): 70 endpoints at
level: write (69 from batches 1-10, +1 HSPT here — LLAN/MVHL/
MVHC/MVLD were already write before batch 11b, so widening them to
Gen doesn't add to this count); 26 with a dated genuinely-unresolved or
scope-limited finding (the 25 through batch 11a plus HECB), plus the one
confirmed live crash (THNL PUT on Gen NX, MAPI-2468). CSCS (composite
section for construction stage) is the one endpoint left in this family,
deferred again — it references a stage-specific section change, a bigger
fixture than this batch's scope.
2026-08-17 — batch 12: db.bridge in full, clean sweep¶
Picked db.bridge next (GSBG/GCMB/CAMB/ULFC, 4 endpoints) — fully
self-contained, no fiber/hinge dependency chain, and small enough to do in
one pass. Three of the four (GSBG/GCMB/CAMB) are Civil-only per
db/bridge.py's own docstring; ULFC answers on Gen too.
One seed: a real /db/GRUP structure group (SG12_SEED, plus a second
SG12_SEED_2 so CAMB's update step has something to switch to) —
GCMB's GRUP_NAME and CAMB's three group-name fields all reference
it by name, GSBG's BODY_ELEM_GRUP_K by its numeric key (12). The
manual's own worked examples use made-up group names for both ("CS_0"
.. "CS_18" for GCMB, "FSM"/"PSC-BN"/"Key-SegK1~K5" for CAMB) —
same lesson as /db/BCCT, used real ones from the start.
First Civil run: GCMB/CAMB/GSBG passed clean immediately. ULFC
failed with "Wrong Field" — the fixture had omitted POINT since the
manual only documents it as "required if TYPE=BEAM," but a direct check
confirmed it's actually required unconditionally: a TYPE="REAC"
payload without it is rejected, POINT=0 clears it. Corrected the
fixture and src/midas_nx/db/bridge.py's POINT field comment to
document this. Re-ran Civil clean (14/14), then Gen (11/11, ULFC only —
the other three are Civil-only by design, not attempted there).
All 4 endpoints flipped to level: write, all confirmed=True — the
first batch in this whole push where every endpoint passed with no
genuinely-unresolved finding left behind. docs/coverage.json and
ROADMAP.md updated; pytest (701) and ruff both clean.
Running total for the write-coverage push (batches 1-12): 74 endpoints at
level: write (70 through batch 11c, +4 here); 26 with a dated
genuinely-unresolved or scope-limited finding (unchanged this batch), plus
the one confirmed live crash (THNL PUT on Gen NX, MAPI-2468).
Caveat — read before acting on this file¶
This is evidence from one MIDASIT account, one product license/edition,
one point in time, not from the manual. It is plausible some of the
20 + 7 "product-only" endpoints above are actually gated by license tier,
Civil/Gen build version, or model state rather than being permanently
unavailable for that product. Do not change any PRODUCTS frozenset in
the SDK based solely on this file. If the same restriction is
independently reproduced (different account, different session, or a
future manual revision adds an explicit product note), that's the trigger
to revisit PRODUCTS for the specific classes involved — cite this file's
date and findings in that future change, and re-verify before trusting it,
since MIDASIT's platform can change between now and then.