Skip to content

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

  1. Read-only smoke test: every DbResource subclass across db/, design/, post/ whose METHODS includes GET and whose PRODUCTS includes the product under test got a live GET call 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."
  2. 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 (ch24 db/design.py, ch25 steel_kds.py, ch26 rc_kds/setup.py, ch27 src_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 PRODUCTS is 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. Their PRODUCTS was {"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's PRODUCTS was the thing that was wrong. Fixed via HYPER_S_ONLY in db/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-line sorted(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 PUT endpoints (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 subsequent GET confirms the value round-trips exactly. Confirmed for steel_kds.UnbracedLength, rc_kds.setup.UnbracedLength, and src_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 documents LANE_ITEMS.CENT_F as merely "optional (AASHTO LRFD only)" — this session's evidence is that once AASHTO LRFD is the selected moving-load code, CENT_F is 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 with CENT_F: 0.1 — succeeded, and GET read 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 the TypedDict), but worth knowing if you hit the same error live: pass a nonzero CENT_F when MVCD.CODE is "AASHTO LRFD".
  • BridgeGirderDiagram.create (/db/GSBG) with a placeholder BODY_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:

  1. 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").
  2. post.result_1.get_reaction_table/get_displacement_table/ get_beam_force_table — first call used load_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 in post/base.py already 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.
  3. The numbers are physically correct, not just structurally valid:
  4. 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).
  5. Displacement at node 2 (free top): DZ = -0.000005 m — negligible axial shortening under self-weight, correct sign (downward).
  6. Beam force: axial force -28.24 kN at the I-end (base), decreasing linearly to 0.00 at 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 kN across both supports (211.823641 each) — consistent with a uniformly distributed self-weight load w = 423.647 / 30 = 14.12 kN/m on a 0.6 × 1.0 m section.
  • 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 subsequent GET confirmed nothing was actually saved) when VEH_DEFAULT was sent as {}. The manual's own worked example for STANDARD_CODE="KS-RB" always populates VEH_DEFAULT with explicit DYN_LOAD_ALLOWANCE/CENT_F values 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 empty VEH_DEFAULT: {} even though every one of its fields is individually documented as optional — populate it per the manual's worked example for your STANDARD_CODE.
  • MovingLoadCase.create (/db/MVLD, TYPE=0 general 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 moment 615.61732.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: MVCDMVHLLLANMVLDdoc.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-ANAL ran 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):

  1. design.rc_kds.setup.ModifyMemberType.create({1: {"TYPE": "COLUMN"}}) — succeeded.
  2. design.rc_kds.rebar.ModifyColumnRebarData.create({1: {"ITEMS": [...]}}) keyed by section number 1 (main bar D22×8, end/center hoop bars D10) — succeeded.
  3. 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).
  4. Re-ran doc.analyze() — succeeded.
  5. Retried CC-ANAL — failed cleanly with {"error": {"message": "failed:LoadCombination"}} (also expected — no load combination existed yet, only a raw load case).
  6. Added db.load_combinations.LoadCombinationGeneral.create({1: {"NAME": "COMB1", "ACTIVE": "ACTIVE", "iTYPE": 0, "vCOMB": [{"ANAL": "ST", "LCNAME": "DL", "FACTOR": 1.2}]}}) — succeeded.
  7. Retried CC-ANAL a third time — this call never returned. Timed out client-side at both 30s (default) and 120s (explicit MidasClient(timeout=120)) with ReadTimeoutError/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):

  1. Fresh minimal model (1 material, 1 section, 2 nodes, 1 element, 1 support, 1 static load case DL, self-weight) + doc.analyze() — all clean.
  2. ModifyMemberTypeCOLUMN, ModifyColumnRebarData (same rebar payload as before), doc.analyze() again — all clean.
  3. CC-ANAL before 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.
  4. Manually written db.load_combinations.LoadCombinationGeneral combo — CC-ANAL retried — failed cleanly and fast again with the same "failed:LoadCombination" message (the manually-entered LCOM-GEN entry apparently isn't recognized as a valid design combination by the RC check module — a separate, milder finding, see below).
  5. 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-generated cLCB1 (1.4(D), ACTIVE: "STRENGTH") and cLCB2 (SERV:(D), ACTIVE: "SERVICE") in /db/LCOM-CONC.
  6. CC-ANAL retried a third time, now with a real, code-recognized design combination in place — hung again, this time with a 40s explicit client timeout.
  7. 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:

  1. 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) and rc_kds.rebar (member rebar) tables were empty for every member, despite the model already having real design-member registrations under a different code.
  2. CC-ANAL on a real, verified-vertical concrete column element with PERFORM_TYPE: "ELEMS" (single element, not "ALL") — failed cleanly and fast: {"error": {"message": "failed:Rebar, BeamData"}}. No hang.
  3. Assigned ModifyMemberType (COLUMN) + ModifyColumnRebarData (same rebar shape as the earlier reproductions) for that element's section, re-ran doc.analyze() (succeeded, no issue at this model's larger scale) — retried CC-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.
  4. 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.
  5. Retried CC-ANAL a 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.

  1. CC-ANAL on a real column element (PERFORM_TYPE: "ELEMS", single element) — failed cleanly: {"error": {"message": " Please perform analysis."}}.
  2. doc.analyze() via the API returned "MIDAS GEN NX command complete", but a subsequent get_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 assume doc.analyze()'s response means results are immediately queryable for large models; confirm with a cheap results call before proceeding, or retry.
  3. CC-ANAL retried 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.

  1. 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.
  2. 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).
  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:

  1. Single wall/story (SELECTIONS: [{"WALL_IDS": {"KEYS": [101]}, "STORY": ["B1"]}]) — returned {"message": "success"} in 3.5s. No stall.
  2. All walls/stories (SELECTIONS omitted) — 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:

  1. BC-ANAL on 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).
  2. ModifyMemberType.update({11: {"TYPE": "BEAM"}}) — succeeded.
  3. BC-ANAL retried — failed cleanly: {"error": {"message": " Please perform analysis."}} (member-type change invalidated prior results, same as CC-ANAL reproduction #1/#4).
  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-ANAL was retried:
  5. BC-ANAL retried 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) — but get_beam_check_table (BC-TABLE) for that same element also then hung repeatedly (multiple retries, up to 30s each), while BC-TABLE for 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):

  1. ModifyMemberType.update({1: {"TYPE": "BEAM"}}) (element 1, a real TYPE: "BEAM" element, section 11) — succeeded.
  2. ModifyBeamRebarData.update({11: {"ITEMS": [...]}}) (keyed by section number 11, matching element 1's SECT) — succeeded.
  3. doc.analyze() — this model is much smaller; completed in 16.4s, and a subsequent get_reaction_table(load_case_names=["DL(ST)"]) call confirmed real, queryable results immediately (no large-model delay this time).
  4. 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).
  5. BC-ANAL on element 1, all preconditions now satisfied on this fresh model — hung again, 60s client timeout, no HTTP response.
  6. 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-ANAL reproductions (#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:

  1. First attempt — failed cleanly and fast: {"error": {"message": " Please perform analysis."}}. Unrelated steel_kds/src_aiksrc2k member-type writes made earlier in this session (while probing whether other design-code modules share the CC-ANAL bug — see above) apparently invalidated the model's analysis results, consistent with the established "any design-parameter write invalidates results" pattern.
  2. Re-ran doc.analyze() — completed in 151.3s (large model, expected per the timeout-guidance note below, not a stall).
  3. WD-ANAL retried 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.
  4. 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 as CC-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 wrongWD-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_steel returned {"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_src succeeded), but SrcBeamSectionData (the rebar-equivalent registration for SRC composite beams) returned {"error": {"message": "Unknown Error"}} on every attempt, and SrcModifyMaterial writes didn't appear to persist (GET came 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/TABLE response'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 (with table_name left 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 a DbResource.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 failed Constraint.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 every DbResource/doc.* call, not just the design-code family.
  • Gen's concrete STANDARD code 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 valid STANDARD strings 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 kN on 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 actual C24/ KS01(RC) preset unit weight is closer to 27.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.py uses 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-model PERFORM_TYPE: "ALL" variant, the exact shape that reproducibly killed the app in the reproductions above
  • BC-ANAL — the beam equivalent, also previously confirmed to hang
  • WC-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_concrete rather 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 requests from a local machine vs. Node fetch from 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 *-ANAL call (the tool uses 25s).
  • Read the *-TABLE back regardless of whether *-ANAL returned. 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 *-TABLE alone 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 PRODUCTS was 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:

{"error": {"message": "MIDAS GEN NX there was an error creating utbl. (ex PostMode ...)"}}

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/STLD before 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_names rather 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/.

after create      : [1, 2, 3, 101]
after delete([101]): []              <- expected [1, 2, 3]

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/ANAL reports a failed solve as {"message": "MIDAS CIVIL NX Analysis failed."} — the same message key a successful call uses for "... command complete", with no error object 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, since message is the normal success carrier elsewhere.
  • /doc/SAVEAS returns {"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/CONS201 + {"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's CONSTRAINT must 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/STLD ignores the "Assign" key and renumbers. Posting under key 7 produced record 2 (NO: 2), the next free slot. /db/NODE honours the key exactly (posting under 77 yields node 77). 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 returns 201 with {"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/new concurrency 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 the license. In order to properly terminate the program, try to re-execute the program, 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/NEW had 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:

  1. 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.
  2. The original documented trigger was literally concurrency. The earlier pre-v2.1 round reproduced this crash with doc/new called 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.
  3. 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/NEW and *-ANAL as 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/NEW and *-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_PATH on every /post/TABLE call 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.comsjj0507). 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/SAVEAS is 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.

POST /db/NMAS  {"Assign": {"3": {"mX": 1.0, "mY": 1.0, "mZ": 1.0}}}

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/NMAS was nowhere near the first write: 100+ calls including dozens of writes had already succeeded, and /db/SDSP had 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

  • NodalMassPayload in db/static_loads.py carries the warning, with the recommendation to use LoadsToMass (/db/LTOM) where the mass can be derived from loads instead.
  • scripts/live_crud_check.py quarantines the case: Case.crashes marks it, it is skipped by default and reported as SKIP, 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/TDMT was not refusing anything server-side — the CODE value was wrong; it and /db/TMAT both 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:

[Error] Errors detected in Pressure Loads Data.(Item:Load Direction)

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:

  1. Seed at the lowest free key, let the case take the next one. Definition tables disagree about honouring the "Assign" key — /db/NODE honours it, /db/STLD and /db/TDME renumber to the next free slot (posting /db/TDME under 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.
  2. Seed steps are per-case dependencies, not per-tier. The first run blocked all 7 props cases behind the one /db/TDMT seed failure, though only /db/TMAT needed 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/TDMT201 + {"error": {"message": "Wrong Field"}} — and by PUT /db/TMAT200 + 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 REGRESSIONconfirmed=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_ONLYRebarCheckInput (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 /info schema answers on Gen, 404s on Civil.
  • /ope/STORY_PARAM and /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/PROJECTSTATUS count ('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 /info schema 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_ONLYgen+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's PARAM[].P_TYPE is 0-indexed (Standard:0, Isotropic:1, Orthotropic:2) and nests user-defined fields under a USER_DEFINED sub-object, vs. non-Hyper-S /db/MATL's 1-indexed P_TYPE with flat fields (ELAST, POISN, ... directly on the param object).
  • /db/IMFM-M1 nests fields under CONCRETE/STEEL sub-objects with different names entirely (UN_CONC_NAME, CONF_CONC_NAME) vs. non-Hyper-S /db/IMFM's flat CONC_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:

  1. InelasticHingePropertyHyperSPss's docstring claimed "PSS" isn't stated in any available source — false. The manual repo's INDEX.md titles the endpoint "Assign Inelastic Hinges — Point Spring Support (Hyper-S)", and 04_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 its NAME field (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.
  2. docs/coverage.json's live_verified.method string was identical for all 8 Hyper-S stubs, which overclaimed schema confirmation for IEHG-{TRUSS,GL,PSS}-M1 — their /info route 404s, so only GET was actually confirmed for them, unlike the other 5. Reworded their method field to say so explicitly, so a future reader of the coverage ledger (or scripts/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):

  1. /db/TDNT (TendonPropertyPayload) was missing bRELAX ("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.
  2. /db/TDPL (TendonPrestressItem) was missing GROUP_NAME ("Group Name", string) — same story: real data has "GROUP_NAME": "PS1" on every item, /info/db/TDPL confirms it, the manual's table doesn't mention it.
  3. /db/TDMT has a third code branch, CODE="EUROPEAN", entirely undocumented. This model's concrete (C40/50) uses it with two dedicated fields, TCODE (int) and bSILICA (bool), neither in the manual's table nor the TypedDict (which only covered CEB-FIP and ACI). GET /info/db/TDMT reveals 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.
  4. /db/MVHL (VehiclePayload) has 11 more country-specific sub-objects the manual never mentions at all. GET /info/db/MVHL lists VEH_FR, VEH_CN, VEH_IN, VEH_CA, VEH_BS, VEH_EUROCODE, VEH_RU, VEH_KSCE_LSD15, VEH_AU, VEH_PL, VEH_ZA alongside the documented VEH_DEFAULT, plus a second load-items array LOAD_ITEMS2. This model's real Eurocode "Load Model 1" vehicle (MVLD_CODE: 11) uses VEH_EUROCODE instead of VEH_DEFAULT and — more surprising — omits STANDARD_CODE entirely, even though the manual's own Specifications table marks it "Required". VEH_EUROCODE itself has ~50 fields (/info schema) covering multiple Eurocode load-model sub-types (LM1/LM2/LM3, rail HSLM-A/B, permit loads); the flat scalar ones are now typed as VehicleEurocodeParams, with the three deeply nested, load-model-specific arrays (LOADCASES/VEHICLES/ PERMIT_LOAD) left untyped, same treatment as SECT_I's precedent. The other 10 country-specific sub-objects are confirmed to exist via /info but not individually typed — a real, larger backlog item if someone needs France/China/India/Canada/etc. moving-load support beyond what already works through VEH_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 confirmed Program 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/PRECAPTURE write 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/*/*-ANAL chapters — need a completed /doc/ANAL run first, and *-ANAL calls have a confirmed history of hanging Gen NX (see the CC-ANAL/BC-ANAL sections 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/LINEBMLDTARGET.METHOD=0 confirmed working on Civil too (matching the manual's worked example, same as Gen); METHOD=1 not re-tried here (already unconfirmed/likely broken on Gen).
  • /ope/AUTOMESH — same element-type restriction found on Gen holds on Civil: 4 boundary elements as BEAM succeeded and generated real PLATE elements; TRUSS was 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-SRC all 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 in ope.py (commit 3d2695e), following the same "confirmed Gen-only, not enforced client-side" pattern already established for STORY_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-22 live_verified record for this endpoint (which already covered both products from live_smoke.py).
  • /post/TABLE (Analysis Result category) — get_reaction_table and get_beam_force_table both 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_table failed 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's Story.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) and CC-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-TABLE and TABLE — 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 only ELEMS set — turned out TABLE_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 matching CODE-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, *-REPORT across all three design families, DREULT, CDESIGN — all require EXPORT_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-ANAL under RC-KDS, BC-ANAL/CC-ANAL under 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 *-ANAL family.
  • /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/STLD shows 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/SECT with vSIZE[0]/vSIZE[1] swapped succeeded, but the STIFF sub-object (Area/Asy/Asz/RXX/RYY/RZZ — note: despite the R prefix these are moment-of-inertia-type values, not radii of gyration; the manual doesn't document this STIFF response 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/3 still showed the old stale values until the user clicked OK to actually commit the dialog.
  • After OK, GET /db/SECT/3 confirmed 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 .mcbx file, then open_project() on that same path — reopened with all 273 nodes intact, confirming the write actually happened (per this SDK's own save_as docstring warning that a rejected path still answers "command complete").
  • close_project() — succeeded, and a follow-up GET /db/NODE correctly answered "The project is not opened" (clean, not a hang) — then open_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 the open_project() round-trip above).
  • export_json()/export_mxt() — both answered command-complete; not independently read back — see below for why IMPORT wasn't attempted, which is the same reason a round-trip verification wasn't either.
  • stage_as() (/doc/STAGAS) — took 3 tries: .mcbx extension 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 own NAME field) with a legacy .mcb extension 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"). Updated stage_as()'s docstring in doc.py so 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):

  1. "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 *-ANAL returns as a clean JSON error; here it's a blocking native dialog instead.
  2. "Unacceptable model for optimal design."
  3. 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:

StructureGroup.create({34: {"NAME": "Girder_All", "E_LIST": list(range(1, 83))}})

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}}
POST /ope/USLC
{"Argument": {"POSITION": "CONC",
              "LCOM_LIST": [{"TYPE": "CONC", "NAME": "cLCB1"}]}}

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 8 TABLE_TYPE values in post.design (BEAMDESIGNFORCES, COLUMNDESIGNFORCES, BRACEDESIGNFORCES, WALLDESIGNFORCES, STEELMEMBERDESIGNFORCES, SRCBEAMDESIGNFORCES, SRCCOLUMNDESIGNFORCES, COLDFORMEDSTEELMEMBERDESIGNFORCES) — every single one answered a clean 200-with-error-body "there was an error creating utbl (ex PostMode ...)". No hang, no crash; verify_connection()/GET /db/NODE confirmed the session healthy after each call, node count unchanged (111) throughout.
  • /DESIGN/RC/KDS-41-20-2022/TABLE, the two not yet covered (BEAMDESIGNFORCES, BRACEDESIGNFORCESCOLUMNDESIGNFORCES was already confirmed clean here back on 2026-07-31) — same clean PostMode error, 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_ECC both 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/moments against a blank doc (no analysis run) got a clean MidasResultError, "MIDAS CIVIL NX Empty Load Case Type". Session stayed alive. Added civil to its live_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/NEWUNITMATL (C24) → SECT (600×600 DBUSER column) → 2×NODEELEM (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 readwrite (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:

  1. SDK-safe path first (sanity check): created Node 1, then NodalMass.create({1: {mX:1, mY:1, mZ:1}}) (rm fields omitted from the call, auto-filled to 0.0 by the SDK before sending) — clean 201, read back correctly, session alive. Cleaned up.
  2. Raw repro, bypassing NodalMass.create() entirely via client.request() directly: created Node 1 and Node 2. Control call — POST /db/NMAS on node 1 with all six fields explicit (mX/mY/mZ + rmX/rmY/rmZ: 0.0) — succeeded in 0.2s. Trigger callPOST /db/NMAS on node 2 with rmX/rmY/rmZ omitted 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/NMAS read back node 2 with rmX: 0, rmY: 0, rmZ: 0the server itself now applies the documented default, rather than reading an uninitialized value. Follow-up verify_connection() and Node.get() both confirmed the session fully responsive immediately after. Test data (NMAS + both nodes) deleted, document back to blank, session still connected throughout 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 itSECT_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 with STORY_WIND_PRESSURE/ SEISMIC_FORCE keyed 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_BY for wind, WEIGHT/ELEV for 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)"]) and get_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 second StaticLoadCase "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 as get_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:

  • SAVEASOPEN (verify) → SAVECLOSEOPEN (reopen): all clean, node count (3) intact through every step. First Gen confirmation for all four endpoints.
  • EXPORT (JSON) succeeded; the IMPORT round 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.
  • EXPORTMXT succeeded; the IMPORTMXT round 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-up GET on /db/SWIND, /db/SSEIS, and /db/STOR showed 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).
  • STAGAS not 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/moving all pass, including /db/NMAS clean via the auto-filled rmX/rmY/rmZ workaround).
  • Gen NX (v2.1, build 08/14/2026): 38/38 — same as 08/12/2026 (no moving tier; 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 via ProjectInfo.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 a pjcf_unlock seed step.
  • /db/STYP: MASS: 0 answers "Wrong Field" — valid values are only 1 (Lumped) or 2 (Consistent) per the manual's own enum, which the first payload didn't respect. Fixed to MASS: 1.
  • /db/SPAN: SPAN_BASE_ITEMS.length must be SPAN_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 own fbld_seed step. Also found live that CO_F's own "NAME" field is read-only, mirroring the linked FBLD record's name: a PUT with NAME="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/SPAN are Civil-only and both pass; NLLP/NLNK/NLNK-M1/CGLP are the 4 failures/blocks).
  • Gen NX (v2.1, build 08/14/2026): 12/15 (STYP-M1/SPAN correctly skipped as Civil-only; NLLP/NLNK/CGLP are the 3 failures/blocks — same NLLP root 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 at level: 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/WVLD are Civil-only; WVLD is the one remaining failure).
  • Gen NX (v2.1, build 08/14/2026): 10/10 — clean pass, exit code 0. PLCB/WVLD correctly 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 (with GROUP_NAME as the material's numeric id "1", not its name — the actual fixture bug found — plus the extra STIFF_COEF_DEFAULT/MASS_COEF_DEFAULT/OPT_*_PROP_DEFAULT fields 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-point vDAY Request 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 id 401 and STRPSSM's at id 9003, not a plain running number like every other endpoint tested today — both describe "PSC/RC 단면" specifically, so the base seed's plain DBUSER rectangular 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, EWSF pass; GRDP/TDMF/RPSC/STRPSSM are the 4 failures).
  • Gen NX (v2.1, build 08/14/2026): 4/7 (STRPSSM/EWSF correctly skipped as Civil-only; GRDP/TDMF/RPSC reproduce 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-SEISMICdb.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/THFC has two seed records ahead of it).
  • /db/THNL: "Unknown Error" — its FUNC_NAME must 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 SDVI example verbatim (DAMPER_TYPE=0, DASHPOT_TYPE=0 — Linear Elastic, not the customized =2 Exponential 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 undocumented INPUT_TYPE_EXFN and per-item EXFN_* fields for the Exponential dashpot type), ruling out a field-name typo.
  • Tried a COMMON-only payload, a payload with INPUT_TYPE_EXFN added, no ITEM array, no DEVICE_TYPE, non-empty COMPANY/PRODUCT_NAME/ TYPE_NUMBER everywhere, INPUT_METHOD=1 (Reference DB), and a single-entry ITEM array instead of 6 — every variant still answered "Wrong Field".
  • Extended the same manual-literal-payload test to SDVE/SDST (Civil) and SDHY/SDIS (Gen) — all 4 failed identically, and a full --tier extras6 run on Gen NX confirmed all 5 fail there too (SDHY/SDIS included 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 fixing PNLD_KEY to the seed's real id. Bisected by removing one field at a time — the manual marks LOAD_GROUP Required, 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's PlaneLoadPayload docstring 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 is NAME/SZ/SRF/SC/FA/FV/DAMP_RATIO only — a completely different, smaller shape than the manual's CODE/METHOD/EPA/SDS/SD1/USER_GROUP/IF/RMF. Sending CODE at 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 one POSL case into two, one payload per product; SeismicLoadParamPayload in db/static_loads.py now 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, POSLPOSL 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 uses TYPE: "EIGEN" (Subspace Iteration) and answered "FREQ_RANGE is required for LANCZOS." — the server doesn't recognize "EIGEN" as a valid TYPE at 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's vBG entries ("BG1"/"BG2", the manual's own example values) answered "Boundary Group not found: BG1" — they have to be real /db/BNGR boundary groups, not free-text labels. Fixed with a bngr_seed step.

Three did not resolve to fixture problems, each a distinct kind of finding:

  • /db/ACTL: TOL never persists as written on Civil NX — POST succeeds and reads back correctly, but a follow-up PUT changing only TOL reads back the original value every time, confirmed even after a full DELETE + fresh POST with a different TOL. On Gen NX the same payload fails outright with "Wrong Field", including a bare {ITER, TOL}-only payload — and Gen's own /info schema turns out to differ from Civil's/the manual's (no CLATS, an undocumented ACWC that 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. Tried OPT_ENABLE_LINE_SEARCH: false explicitly (same error) and a guessed LINE_SEARCH_OPTION: "AUTO" alongside true (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's THETA field has the same "write doesn't stick" symptom as ACTL's TOL — POST succeeds with no error body, but the immediate GET reads THETA back as 0, not the 1 sent, 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 /info on 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, BCCT pass; ACTL, HHCT, MVCT, NLCT are the 4 failures).
  • Gen NX: 7/9 (same 5 plus HHCT and NLCT; ACTL and MVCT are 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/MADO answers {"message": ""} — HTTP success, no error body at all — but the record is absent from a follow-up GET, every time.
  • Reproduced with several payload variants: MATL/PROP pointed at the base model's real material/section ids (1/1), SUB_TYPE 1 and 2, TYPE 3 (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 (ETFCCCFCHSFCHAHSHECBHSTG), 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 with hsfc_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/BCCT lesson 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's PLATE) — same error, just renumbered to element 4. Heat-of-hydration is normally applied to mass concrete (piers, footings) modelled as SOLID elements, which the base seed model doesn't build — most likely a scope gap in this fixture rather than a confirmed product defect, since a real SOLID element 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 except ITEMS succeeds individually (each answering the ordinary "Wrong Field" for an incomplete payload), but adding ITEMS — 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 confirms ITEMS is 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/EPST etc.

Re-run clean on both products after the HSFC/HSTG fixes:

  • Civil NX: 5/7 (ETFC, CCFC, HSFC, STBK, HSTG pass; HAHS and HPCE are the 2 failures).
  • Gen NX: same 5/7 split, confirming HAHS/HPCE aren'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 rejected TYPE="EIGEN" attempt — except this run used the fixed TYPE="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 named FREQ_RANGE that FRMIN/FRMAX don't satisfy.
  • BCCT: "Boundary Group not found: BG1" — the exact error the bngr_seed fix (creating real /db/BNGR groups named BG1/BG2) was supposed to clear, and did clear on Gen (extras8_gen.json, same day, 5 minutes later, both cases ok). 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/HAHS from 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. Given HAHS hit the identical wall and heat-of-hydration boundary conditions are normally applied to mass-concrete SOLID elements (piers, footings), this reads as the same fixture scope gap — the base model only builds frame and plate elements, no SOLID — 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.