[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-nvidia-rest-flow-grpc-proxy":3,"mdc-mhr4tn-key":30,"related-org-nvidia-rest-flow-grpc-proxy":765,"related-repo-nvidia-rest-flow-grpc-proxy":926},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":19,"repoUrl":20,"updatedAt":21,"license":22,"forks":23,"topics":24,"repo":25,"sourceUrl":28,"mdContent":29},"rest-flow-grpc-proxy","migrate REST API endpoints to gRPC","Build or migrate infra-controller REST API endpoints that call on-site Flow through the generic Flow gRPC proxy. Use when working on REST-to-Flow operations, ExecuteFlowGRPC, grpcproxy, v1.Flow methods, or migrating bespoke TaskRun\u002FFlow workflows to the gRPC proxy.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},"nvidia","NVIDIA","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fnvidia.png",[12,16],{"name":13,"slug":14,"type":15},"REST API","rest-api","tag",{"name":17,"slug":18,"type":15},"API Development","api-development",226,"https:\u002F\u002Fgithub.com\u002FNVIDIA\u002Finfra-controller","2026-08-31T09:19:19.794717",null,150,[],{"repoUrl":20,"stars":19,"forks":23,"topics":26,"description":27},[],"NVIDIA Infra Controller - Hardware Lifecycle Management and multitenant networking","https:\u002F\u002Fgithub.com\u002FNVIDIA\u002Finfra-controller\u002Ftree\u002FHEAD\u002Frest-api\u002Fskills\u002Frest-flow-grpc-proxy","---\nname: rest-flow-grpc-proxy\ndescription: Build or migrate infra-controller REST API endpoints that call on-site Flow through the generic Flow gRPC proxy. Use when working on REST-to-Flow operations, ExecuteFlowGRPC, grpcproxy, v1.Flow methods, or migrating bespoke TaskRun\u002FFlow workflows to the gRPC proxy.\n---\n\n# REST Flow gRPC Proxy Skill\n\nUse this guidance when building or converting `infra-controller` REST API\nendpoints that need to call on-site Flow through the generic Flow gRPC proxy.\n\n## Current Proxy Contract\n\n- Cloud helper: `rest-api\u002Fapi\u002Fpkg\u002Fapi\u002Fhandler\u002Futil\u002Fcommon\u002Fgrpcproxy.go`,\n  `ExecuteFlowGRPC`.\n- Shared contract: `rest-api\u002Fcommon\u002Fpkg\u002Fgrpcproxy\u002Fgrpcproxy.go`, with\n  `grpcproxy.Request` and `grpcproxy.Response`. Flow and Core share every layer\n  of the proxy; the backend is named by `grpcproxy.Flow`, and each layer keeps a\n  per-backend wrapper only because Temporal dispatches on the registered\n  workflow and activity names.\n- Site workflow\u002Factivity: `InvokeFlowGRPC` and `InvokeFlowGRPCOnSite`.\n- Site Flow invocation: `FlowGrpcClient.InvokeJSON`.\n- Temporal transport payload: protojson, so non-secret request fields and\n  responses remain readable in Temporal UI.\n- Secret transport payload: selected top-level protojson fields are redacted\n  from `RequestJSON` and carried separately in `EncryptedSecrets`.\n  `grpcproxy.RedactSecrets` \u002F `grpcproxy.MergeSecrets` do the splitting and are\n  shared with the Core proxy.\n- Final site-to-Flow call: normal binary gRPC. The JSON step is only the\n  generic Temporal payload representation.\n\n## Critical Difference From CoreProxy\n\n`ExecuteCoreGRPC` always generates a fresh workflow ID\n(`core-grpc-{Method}-{uuid}`) and does not set `USE_EXISTING`.\n\n`ExecuteFlowGRPC` requires the caller to supply:\n\n1. `workflowID` — deterministic for read\u002Flist dedup, fresh UUID for creates.\n2. `conflictPolicy` — the two travel together. A deterministic ID takes\n   `WORKFLOW_ID_CONFLICT_POLICY_USE_EXISTING` so identical in-flight requests\n   coalesce onto one Flow call; a fresh ID takes\n   `WORKFLOW_ID_CONFLICT_POLICY_UNSPECIFIED`, matching `ExecuteCoreGRPC`, whose\n   IDs are always fresh.\n\nTwo helpers in `common` carry this for every migrated handler:\n`common.FlowWorkflowID` applies the transport namespace, and\n`common.ProxyFlowGRPC` dispatches and renders the failure as an Echo response.\nUse `ExecuteFlowGRPC` directly only in helpers that hand the error back to a\ncaller instead of rendering a response, as `resolveTrayIDsBySlot` does; return\nthe `*cutil.APIError` unwrapped so the status the proxy chose survives.\n\nKinds of ID derivation that must stay in the handler, using the TaskRun\nendpoints as the worked example:\n\n- A read whose response shape depends on a query flag must encode that flag, so\n  `GetTaskRun` needs `includeStats` in the ID\n  (`flow-grpc-task-run-get-{runID}-{true|false}`). Attaching to an execution\n  started with the other value would return stats presence that contradicts the\n  request.\n- A list must hash its query parameters (`QueryParamHash`) into the ID, so two\n  different filters never coalesce.\n- A create must never coalesce at all, so `CreateTaskRun` uses a fresh UUID\n  (`flow-grpc-task-run-create-{uuid}`) and `USE_EXISTING` can never match.\n\nPrefix deterministic IDs with the transport (`flow-grpc-`). A deterministic ID\nplus `USE_EXISTING` attaches to whatever execution already holds that name, so\nan ID shared with a different workflow type — a bespoke predecessor still\nrunning during a rollout, for instance — yields a result payload this proxy\ncannot decode.\n\n## Timeout Ladder\n\nThe ladder is bounded from the outside, not chosen from the on-site budget:\n`server.WriteTimeout` is a deadline on the response write, so a handler that\nanswers later than that cannot deliver its answer. Keep\n`ActivityStartToCloseTimeout` \u003C `grpcproxy.WorkflowExecutionTimeout` \u003C\n`cutil.WorkflowContextTimeout` \u003C `server.WriteTimeout`, currently\n40s \u003C 45s \u003C 50s \u003C 60s. `Test_ProxyTimeoutsFitWriteTimeout` in\n`api\u002Finternal\u002Fserver` guards the outer bound. Raising the on-site budget means\nraising the server and load-balancer timeouts first.\n\nBecause the workflow timeout sits inside the caller's context timeout, a timeout\nthat Temporal itself reports comes from an execution that has closed. The ladder\ndoes not cover the other way a caller loses its result: `wfCtx` derives from the\nrequest context, so a client disconnect or a shorter upstream deadline can end\nthe wait while the execution runs on. `wfCtx.Err()` is the only evidence that\nthis happened, which is why a `context.DeadlineExceeded` in the workflow result\nalone does not classify as it — it can come from inside the execution.\n\n## Before Coding\n\nConfirm these details before editing:\n\n- REST operation path, method, auth role, org\u002Fsite scoping, Flow enablement\n  check, request model, response model, and expected status code.\n- Target Flow method, usually `\u002Fv1.Flow\u002F\u003CMethod>`, and whether it is unary.\n  The proxy does not support streaming methods.\n- Typed protobuf request and optional typed protobuf response.\n- Workflow ID derivation and conflict policy for this call.\n- Secret fields that must not appear in Temporal history (top-level protojson\n  field names).\n- Whether the call fits in the proxy's budget. The activity is cut off at\n  `grpcproxy.ActivityStartToCloseTimeout`, currently 40s. The bespoke workflows\n  declared 2 minutes, and bring-up and firmware 5 minutes, but every Flow caller\n  bounded itself with the 50s `cutil.WorkflowContextTimeout`, so no declared\n  budget was reachable. Migrating still narrows the window: a Flow call that took\n  41-49s used to succeed and now fails.\n- Whether the call tolerates losing an activity retry. `InvokeFlowGRPC` runs the\n  activity with `MaximumAttempts: 1`, so a transient Flow error surfaces to the\n  client instead of being retried. Bespoke workflows that allowed a second\n  attempt lose it on migration, and the retry policy lives in the site workflow,\n  so making it configurable takes another agent release to take effect.\n\n## Implementation Workflow\n\n1. Keep auth, tenant\u002Forg membership, site lookup, Flow enablement, role checks,\n   request validation, and REST semantics in the REST handler.\n2. Build the typed Flow protobuf request before calling the proxy.\n3. Call `common.ExecuteFlowGRPC(ctx, siteTemporalClient, fullMethod, reqProto,\n   respProtoOrNil, workflowID, conflictPolicy, siteIDSecretKey, secretFields...)`.\n   Passing `secretFields` requires a non-empty `siteIDSecretKey`; the helper\n   rejects the combination rather than send the fields unredacted.\n4. Return `StatusGatewayTimeout` as it comes. Do not call\n   `TerminateWorkflowOnTimeOut`. When Temporal reports the timeout the execution\n   has already closed, and terminating a closed execution fails and reports a\n   data desync that did not happen. When the caller stopped waiting instead, the\n   execution may still be running, and terminating it still does not help: the\n   activity does not heartbeat, so Temporal cannot deliver cancellation while\n   its Flow RPC is in progress, and terminating only the workflow discards the\n   result without stopping that RPC. For a deterministic ID with `USE_EXISTING`\n   it also frees the ID, so a retried request starts a duplicate mutation\n   instead of attaching to the call already in flight. An abandoned execution\n   stays bounded by the 45s and 40s timeouts.\n5. Return a curated REST response. Do not expose Flow protobufs or secret\n   fields directly unless the API contract already does.\n6. For a new public REST endpoint, register the route and update OpenAPI. For a\n   migration from a bespoke workflow to the generic proxy, keep the REST\n   contract and the parameter-derivation rules for the workflow ID unchanged,\n   but namespace the resulting ID by transport as described above. The derived\n   ID string itself must change, otherwise the proxy and the workflow it\n   replaces can attach to each other.\n\n## Rollout Requirement\n\nA workflow type is known only to the workers that registered it. The cloud API\nand each site's agent ship as separate Helm releases (`nico-rest` and\n`nico-rest-site-agent`) and cannot be upgraded atomically, and Temporal accepts\na submission for a type no worker knows: the execution is created, no worker can\nadvance it, and the caller sees only a timeout after\n`grpcproxy.WorkflowExecutionTimeout`.\n\nSo a handler may not start dispatching through `InvokeFlowGRPC` in the same\nrelease that first registers it on the site agent. Register the proxy first,\nwait for every site to run that agent, and switch handlers in a later release.\nRetire the workflow a migration replaces in a third release, once no supported\ncloud release still submits it.\n\nWhen adding a Flow endpoint, reach for the proxy: there is no longer a bespoke\nFlow workflow to copy, and adding one would reintroduce the per-method\nregistration this replaced.\n",{"data":31,"body":32},{"name":4,"description":6},{"type":33,"children":34},"root",[35,44,59,66,207,213,239,249,298,350,355,428,448,454,513,542,548,553,629,635,715,721,748,760],{"type":36,"tag":37,"props":38,"children":40},"element","h1",{"id":39},"rest-flow-grpc-proxy-skill",[41],{"type":42,"value":43},"text","REST Flow gRPC Proxy Skill",{"type":36,"tag":45,"props":46,"children":47},"p",{},[48,50,57],{"type":42,"value":49},"Use this guidance when building or converting ",{"type":36,"tag":51,"props":52,"children":54},"code",{"className":53},[],[55],{"type":42,"value":56},"infra-controller",{"type":42,"value":58}," REST API\nendpoints that need to call on-site Flow through the generic Flow gRPC proxy.",{"type":36,"tag":60,"props":61,"children":63},"h2",{"id":62},"current-proxy-contract",[64],{"type":42,"value":65},"Current Proxy Contract",{"type":36,"tag":67,"props":68,"children":69},"ul",{},[70,92,129,148,160,165,202],{"type":36,"tag":71,"props":72,"children":73},"li",{},[74,76,82,84,90],{"type":42,"value":75},"Cloud helper: ",{"type":36,"tag":51,"props":77,"children":79},{"className":78},[],[80],{"type":42,"value":81},"rest-api\u002Fapi\u002Fpkg\u002Fapi\u002Fhandler\u002Futil\u002Fcommon\u002Fgrpcproxy.go",{"type":42,"value":83},",\n",{"type":36,"tag":51,"props":85,"children":87},{"className":86},[],[88],{"type":42,"value":89},"ExecuteFlowGRPC",{"type":42,"value":91},".",{"type":36,"tag":71,"props":93,"children":94},{},[95,97,103,105,111,113,119,121,127],{"type":42,"value":96},"Shared contract: ",{"type":36,"tag":51,"props":98,"children":100},{"className":99},[],[101],{"type":42,"value":102},"rest-api\u002Fcommon\u002Fpkg\u002Fgrpcproxy\u002Fgrpcproxy.go",{"type":42,"value":104},", with\n",{"type":36,"tag":51,"props":106,"children":108},{"className":107},[],[109],{"type":42,"value":110},"grpcproxy.Request",{"type":42,"value":112}," and ",{"type":36,"tag":51,"props":114,"children":116},{"className":115},[],[117],{"type":42,"value":118},"grpcproxy.Response",{"type":42,"value":120},". Flow and Core share every layer\nof the proxy; the backend is named by ",{"type":36,"tag":51,"props":122,"children":124},{"className":123},[],[125],{"type":42,"value":126},"grpcproxy.Flow",{"type":42,"value":128},", and each layer keeps a\nper-backend wrapper only because Temporal dispatches on the registered\nworkflow and activity names.",{"type":36,"tag":71,"props":130,"children":131},{},[132,134,140,141,147],{"type":42,"value":133},"Site workflow\u002Factivity: ",{"type":36,"tag":51,"props":135,"children":137},{"className":136},[],[138],{"type":42,"value":139},"InvokeFlowGRPC",{"type":42,"value":112},{"type":36,"tag":51,"props":142,"children":144},{"className":143},[],[145],{"type":42,"value":146},"InvokeFlowGRPCOnSite",{"type":42,"value":91},{"type":36,"tag":71,"props":149,"children":150},{},[151,153,159],{"type":42,"value":152},"Site Flow invocation: ",{"type":36,"tag":51,"props":154,"children":156},{"className":155},[],[157],{"type":42,"value":158},"FlowGrpcClient.InvokeJSON",{"type":42,"value":91},{"type":36,"tag":71,"props":161,"children":162},{},[163],{"type":42,"value":164},"Temporal transport payload: protojson, so non-secret request fields and\nresponses remain readable in Temporal UI.",{"type":36,"tag":71,"props":166,"children":167},{},[168,170,176,178,184,186,192,194,200],{"type":42,"value":169},"Secret transport payload: selected top-level protojson fields are redacted\nfrom ",{"type":36,"tag":51,"props":171,"children":173},{"className":172},[],[174],{"type":42,"value":175},"RequestJSON",{"type":42,"value":177}," and carried separately in ",{"type":36,"tag":51,"props":179,"children":181},{"className":180},[],[182],{"type":42,"value":183},"EncryptedSecrets",{"type":42,"value":185},".\n",{"type":36,"tag":51,"props":187,"children":189},{"className":188},[],[190],{"type":42,"value":191},"grpcproxy.RedactSecrets",{"type":42,"value":193}," \u002F ",{"type":36,"tag":51,"props":195,"children":197},{"className":196},[],[198],{"type":42,"value":199},"grpcproxy.MergeSecrets",{"type":42,"value":201}," do the splitting and are\nshared with the Core proxy.",{"type":36,"tag":71,"props":203,"children":204},{},[205],{"type":42,"value":206},"Final site-to-Flow call: normal binary gRPC. The JSON step is only the\ngeneric Temporal payload representation.",{"type":36,"tag":60,"props":208,"children":210},{"id":209},"critical-difference-from-coreproxy",[211],{"type":42,"value":212},"Critical Difference From CoreProxy",{"type":36,"tag":45,"props":214,"children":215},{},[216,222,224,230,232,238],{"type":36,"tag":51,"props":217,"children":219},{"className":218},[],[220],{"type":42,"value":221},"ExecuteCoreGRPC",{"type":42,"value":223}," always generates a fresh workflow ID\n(",{"type":36,"tag":51,"props":225,"children":227},{"className":226},[],[228],{"type":42,"value":229},"core-grpc-{Method}-{uuid}",{"type":42,"value":231},") and does not set ",{"type":36,"tag":51,"props":233,"children":235},{"className":234},[],[236],{"type":42,"value":237},"USE_EXISTING",{"type":42,"value":91},{"type":36,"tag":45,"props":240,"children":241},{},[242,247],{"type":36,"tag":51,"props":243,"children":245},{"className":244},[],[246],{"type":42,"value":89},{"type":42,"value":248}," requires the caller to supply:",{"type":36,"tag":250,"props":251,"children":252},"ol",{},[253,264],{"type":36,"tag":71,"props":254,"children":255},{},[256,262],{"type":36,"tag":51,"props":257,"children":259},{"className":258},[],[260],{"type":42,"value":261},"workflowID",{"type":42,"value":263}," — deterministic for read\u002Flist dedup, fresh UUID for creates.",{"type":36,"tag":71,"props":265,"children":266},{},[267,273,275,281,283,289,291,296],{"type":36,"tag":51,"props":268,"children":270},{"className":269},[],[271],{"type":42,"value":272},"conflictPolicy",{"type":42,"value":274}," — the two travel together. A deterministic ID takes\n",{"type":36,"tag":51,"props":276,"children":278},{"className":277},[],[279],{"type":42,"value":280},"WORKFLOW_ID_CONFLICT_POLICY_USE_EXISTING",{"type":42,"value":282}," so identical in-flight requests\ncoalesce onto one Flow call; a fresh ID takes\n",{"type":36,"tag":51,"props":284,"children":286},{"className":285},[],[287],{"type":42,"value":288},"WORKFLOW_ID_CONFLICT_POLICY_UNSPECIFIED",{"type":42,"value":290},", matching ",{"type":36,"tag":51,"props":292,"children":294},{"className":293},[],[295],{"type":42,"value":221},{"type":42,"value":297},", whose\nIDs are always fresh.",{"type":36,"tag":45,"props":299,"children":300},{},[301,303,309,311,317,319,325,327,332,334,340,342,348],{"type":42,"value":302},"Two helpers in ",{"type":36,"tag":51,"props":304,"children":306},{"className":305},[],[307],{"type":42,"value":308},"common",{"type":42,"value":310}," carry this for every migrated handler:\n",{"type":36,"tag":51,"props":312,"children":314},{"className":313},[],[315],{"type":42,"value":316},"common.FlowWorkflowID",{"type":42,"value":318}," applies the transport namespace, and\n",{"type":36,"tag":51,"props":320,"children":322},{"className":321},[],[323],{"type":42,"value":324},"common.ProxyFlowGRPC",{"type":42,"value":326}," dispatches and renders the failure as an Echo response.\nUse ",{"type":36,"tag":51,"props":328,"children":330},{"className":329},[],[331],{"type":42,"value":89},{"type":42,"value":333}," directly only in helpers that hand the error back to a\ncaller instead of rendering a response, as ",{"type":36,"tag":51,"props":335,"children":337},{"className":336},[],[338],{"type":42,"value":339},"resolveTrayIDsBySlot",{"type":42,"value":341}," does; return\nthe ",{"type":36,"tag":51,"props":343,"children":345},{"className":344},[],[346],{"type":42,"value":347},"*cutil.APIError",{"type":42,"value":349}," unwrapped so the status the proxy chose survives.",{"type":36,"tag":45,"props":351,"children":352},{},[353],{"type":42,"value":354},"Kinds of ID derivation that must stay in the handler, using the TaskRun\nendpoints as the worked example:",{"type":36,"tag":67,"props":356,"children":357},{},[358,387,400],{"type":36,"tag":71,"props":359,"children":360},{},[361,363,369,371,377,379,385],{"type":42,"value":362},"A read whose response shape depends on a query flag must encode that flag, so\n",{"type":36,"tag":51,"props":364,"children":366},{"className":365},[],[367],{"type":42,"value":368},"GetTaskRun",{"type":42,"value":370}," needs ",{"type":36,"tag":51,"props":372,"children":374},{"className":373},[],[375],{"type":42,"value":376},"includeStats",{"type":42,"value":378}," in the ID\n(",{"type":36,"tag":51,"props":380,"children":382},{"className":381},[],[383],{"type":42,"value":384},"flow-grpc-task-run-get-{runID}-{true|false}",{"type":42,"value":386},"). Attaching to an execution\nstarted with the other value would return stats presence that contradicts the\nrequest.",{"type":36,"tag":71,"props":388,"children":389},{},[390,392,398],{"type":42,"value":391},"A list must hash its query parameters (",{"type":36,"tag":51,"props":393,"children":395},{"className":394},[],[396],{"type":42,"value":397},"QueryParamHash",{"type":42,"value":399},") into the ID, so two\ndifferent filters never coalesce.",{"type":36,"tag":71,"props":401,"children":402},{},[403,405,411,413,419,421,426],{"type":42,"value":404},"A create must never coalesce at all, so ",{"type":36,"tag":51,"props":406,"children":408},{"className":407},[],[409],{"type":42,"value":410},"CreateTaskRun",{"type":42,"value":412}," uses a fresh UUID\n(",{"type":36,"tag":51,"props":414,"children":416},{"className":415},[],[417],{"type":42,"value":418},"flow-grpc-task-run-create-{uuid}",{"type":42,"value":420},") and ",{"type":36,"tag":51,"props":422,"children":424},{"className":423},[],[425],{"type":42,"value":237},{"type":42,"value":427}," can never match.",{"type":36,"tag":45,"props":429,"children":430},{},[431,433,439,441,446],{"type":42,"value":432},"Prefix deterministic IDs with the transport (",{"type":36,"tag":51,"props":434,"children":436},{"className":435},[],[437],{"type":42,"value":438},"flow-grpc-",{"type":42,"value":440},"). A deterministic ID\nplus ",{"type":36,"tag":51,"props":442,"children":444},{"className":443},[],[445],{"type":42,"value":237},{"type":42,"value":447}," attaches to whatever execution already holds that name, so\nan ID shared with a different workflow type — a bespoke predecessor still\nrunning during a rollout, for instance — yields a result payload this proxy\ncannot decode.",{"type":36,"tag":60,"props":449,"children":451},{"id":450},"timeout-ladder",[452],{"type":42,"value":453},"Timeout Ladder",{"type":36,"tag":45,"props":455,"children":456},{},[457,459,465,467,473,475,481,483,489,490,495,497,503,505,511],{"type":42,"value":458},"The ladder is bounded from the outside, not chosen from the on-site budget:\n",{"type":36,"tag":51,"props":460,"children":462},{"className":461},[],[463],{"type":42,"value":464},"server.WriteTimeout",{"type":42,"value":466}," is a deadline on the response write, so a handler that\nanswers later than that cannot deliver its answer. Keep\n",{"type":36,"tag":51,"props":468,"children":470},{"className":469},[],[471],{"type":42,"value":472},"ActivityStartToCloseTimeout",{"type":42,"value":474}," \u003C ",{"type":36,"tag":51,"props":476,"children":478},{"className":477},[],[479],{"type":42,"value":480},"grpcproxy.WorkflowExecutionTimeout",{"type":42,"value":482}," \u003C\n",{"type":36,"tag":51,"props":484,"children":486},{"className":485},[],[487],{"type":42,"value":488},"cutil.WorkflowContextTimeout",{"type":42,"value":474},{"type":36,"tag":51,"props":491,"children":493},{"className":492},[],[494],{"type":42,"value":464},{"type":42,"value":496},", currently\n40s \u003C 45s \u003C 50s \u003C 60s. ",{"type":36,"tag":51,"props":498,"children":500},{"className":499},[],[501],{"type":42,"value":502},"Test_ProxyTimeoutsFitWriteTimeout",{"type":42,"value":504}," in\n",{"type":36,"tag":51,"props":506,"children":508},{"className":507},[],[509],{"type":42,"value":510},"api\u002Finternal\u002Fserver",{"type":42,"value":512}," guards the outer bound. Raising the on-site budget means\nraising the server and load-balancer timeouts first.",{"type":36,"tag":45,"props":514,"children":515},{},[516,518,524,526,532,534,540],{"type":42,"value":517},"Because the workflow timeout sits inside the caller's context timeout, a timeout\nthat Temporal itself reports comes from an execution that has closed. The ladder\ndoes not cover the other way a caller loses its result: ",{"type":36,"tag":51,"props":519,"children":521},{"className":520},[],[522],{"type":42,"value":523},"wfCtx",{"type":42,"value":525}," derives from the\nrequest context, so a client disconnect or a shorter upstream deadline can end\nthe wait while the execution runs on. ",{"type":36,"tag":51,"props":527,"children":529},{"className":528},[],[530],{"type":42,"value":531},"wfCtx.Err()",{"type":42,"value":533}," is the only evidence that\nthis happened, which is why a ",{"type":36,"tag":51,"props":535,"children":537},{"className":536},[],[538],{"type":42,"value":539},"context.DeadlineExceeded",{"type":42,"value":541}," in the workflow result\nalone does not classify as it — it can come from inside the execution.",{"type":36,"tag":60,"props":543,"children":545},{"id":544},"before-coding",[546],{"type":42,"value":547},"Before Coding",{"type":36,"tag":45,"props":549,"children":550},{},[551],{"type":42,"value":552},"Confirm these details before editing:",{"type":36,"tag":67,"props":554,"children":555},{},[556,561,574,579,584,589,609],{"type":36,"tag":71,"props":557,"children":558},{},[559],{"type":42,"value":560},"REST operation path, method, auth role, org\u002Fsite scoping, Flow enablement\ncheck, request model, response model, and expected status code.",{"type":36,"tag":71,"props":562,"children":563},{},[564,566,572],{"type":42,"value":565},"Target Flow method, usually ",{"type":36,"tag":51,"props":567,"children":569},{"className":568},[],[570],{"type":42,"value":571},"\u002Fv1.Flow\u002F\u003CMethod>",{"type":42,"value":573},", and whether it is unary.\nThe proxy does not support streaming methods.",{"type":36,"tag":71,"props":575,"children":576},{},[577],{"type":42,"value":578},"Typed protobuf request and optional typed protobuf response.",{"type":36,"tag":71,"props":580,"children":581},{},[582],{"type":42,"value":583},"Workflow ID derivation and conflict policy for this call.",{"type":36,"tag":71,"props":585,"children":586},{},[587],{"type":42,"value":588},"Secret fields that must not appear in Temporal history (top-level protojson\nfield names).",{"type":36,"tag":71,"props":590,"children":591},{},[592,594,600,602,607],{"type":42,"value":593},"Whether the call fits in the proxy's budget. The activity is cut off at\n",{"type":36,"tag":51,"props":595,"children":597},{"className":596},[],[598],{"type":42,"value":599},"grpcproxy.ActivityStartToCloseTimeout",{"type":42,"value":601},", currently 40s. The bespoke workflows\ndeclared 2 minutes, and bring-up and firmware 5 minutes, but every Flow caller\nbounded itself with the 50s ",{"type":36,"tag":51,"props":603,"children":605},{"className":604},[],[606],{"type":42,"value":488},{"type":42,"value":608},", so no declared\nbudget was reachable. Migrating still narrows the window: a Flow call that took\n41-49s used to succeed and now fails.",{"type":36,"tag":71,"props":610,"children":611},{},[612,614,619,621,627],{"type":42,"value":613},"Whether the call tolerates losing an activity retry. ",{"type":36,"tag":51,"props":615,"children":617},{"className":616},[],[618],{"type":42,"value":139},{"type":42,"value":620}," runs the\nactivity with ",{"type":36,"tag":51,"props":622,"children":624},{"className":623},[],[625],{"type":42,"value":626},"MaximumAttempts: 1",{"type":42,"value":628},", so a transient Flow error surfaces to the\nclient instead of being retried. Bespoke workflows that allowed a second\nattempt lose it on migration, and the retry policy lives in the site workflow,\nso making it configurable takes another agent release to take effect.",{"type":36,"tag":60,"props":630,"children":632},{"id":631},"implementation-workflow",[633],{"type":42,"value":634},"Implementation Workflow",{"type":36,"tag":250,"props":636,"children":637},{},[638,643,648,677,705,710],{"type":36,"tag":71,"props":639,"children":640},{},[641],{"type":42,"value":642},"Keep auth, tenant\u002Forg membership, site lookup, Flow enablement, role checks,\nrequest validation, and REST semantics in the REST handler.",{"type":36,"tag":71,"props":644,"children":645},{},[646],{"type":42,"value":647},"Build the typed Flow protobuf request before calling the proxy.",{"type":36,"tag":71,"props":649,"children":650},{},[651,653,659,661,667,669,675],{"type":42,"value":652},"Call ",{"type":36,"tag":51,"props":654,"children":656},{"className":655},[],[657],{"type":42,"value":658},"common.ExecuteFlowGRPC(ctx, siteTemporalClient, fullMethod, reqProto, respProtoOrNil, workflowID, conflictPolicy, siteIDSecretKey, secretFields...)",{"type":42,"value":660},".\nPassing ",{"type":36,"tag":51,"props":662,"children":664},{"className":663},[],[665],{"type":42,"value":666},"secretFields",{"type":42,"value":668}," requires a non-empty ",{"type":36,"tag":51,"props":670,"children":672},{"className":671},[],[673],{"type":42,"value":674},"siteIDSecretKey",{"type":42,"value":676},"; the helper\nrejects the combination rather than send the fields unredacted.",{"type":36,"tag":71,"props":678,"children":679},{},[680,682,688,690,696,698,703],{"type":42,"value":681},"Return ",{"type":36,"tag":51,"props":683,"children":685},{"className":684},[],[686],{"type":42,"value":687},"StatusGatewayTimeout",{"type":42,"value":689}," as it comes. Do not call\n",{"type":36,"tag":51,"props":691,"children":693},{"className":692},[],[694],{"type":42,"value":695},"TerminateWorkflowOnTimeOut",{"type":42,"value":697},". When Temporal reports the timeout the execution\nhas already closed, and terminating a closed execution fails and reports a\ndata desync that did not happen. When the caller stopped waiting instead, the\nexecution may still be running, and terminating it still does not help: the\nactivity does not heartbeat, so Temporal cannot deliver cancellation while\nits Flow RPC is in progress, and terminating only the workflow discards the\nresult without stopping that RPC. For a deterministic ID with ",{"type":36,"tag":51,"props":699,"children":701},{"className":700},[],[702],{"type":42,"value":237},{"type":42,"value":704},"\nit also frees the ID, so a retried request starts a duplicate mutation\ninstead of attaching to the call already in flight. An abandoned execution\nstays bounded by the 45s and 40s timeouts.",{"type":36,"tag":71,"props":706,"children":707},{},[708],{"type":42,"value":709},"Return a curated REST response. Do not expose Flow protobufs or secret\nfields directly unless the API contract already does.",{"type":36,"tag":71,"props":711,"children":712},{},[713],{"type":42,"value":714},"For a new public REST endpoint, register the route and update OpenAPI. For a\nmigration from a bespoke workflow to the generic proxy, keep the REST\ncontract and the parameter-derivation rules for the workflow ID unchanged,\nbut namespace the resulting ID by transport as described above. The derived\nID string itself must change, otherwise the proxy and the workflow it\nreplaces can attach to each other.",{"type":36,"tag":60,"props":716,"children":718},{"id":717},"rollout-requirement",[719],{"type":42,"value":720},"Rollout Requirement",{"type":36,"tag":45,"props":722,"children":723},{},[724,726,732,734,740,742,747],{"type":42,"value":725},"A workflow type is known only to the workers that registered it. The cloud API\nand each site's agent ship as separate Helm releases (",{"type":36,"tag":51,"props":727,"children":729},{"className":728},[],[730],{"type":42,"value":731},"nico-rest",{"type":42,"value":733}," and\n",{"type":36,"tag":51,"props":735,"children":737},{"className":736},[],[738],{"type":42,"value":739},"nico-rest-site-agent",{"type":42,"value":741},") and cannot be upgraded atomically, and Temporal accepts\na submission for a type no worker knows: the execution is created, no worker can\nadvance it, and the caller sees only a timeout after\n",{"type":36,"tag":51,"props":743,"children":745},{"className":744},[],[746],{"type":42,"value":480},{"type":42,"value":91},{"type":36,"tag":45,"props":749,"children":750},{},[751,753,758],{"type":42,"value":752},"So a handler may not start dispatching through ",{"type":36,"tag":51,"props":754,"children":756},{"className":755},[],[757],{"type":42,"value":139},{"type":42,"value":759}," in the same\nrelease that first registers it on the site agent. Register the proxy first,\nwait for every site to run that agent, and switch handlers in a later release.\nRetire the workflow a migration replaces in a third release, once no supported\ncloud release still submits it.",{"type":36,"tag":45,"props":761,"children":762},{},[763],{"type":42,"value":764},"When adding a Flow endpoint, reach for the proxy: there is no longer a bespoke\nFlow workflow to copy, and adding one would reintroduce the per-method\nregistration this replaced.",{"items":766,"total":925},[767,785,803,814,826,840,853,867,880,891,905,914],{"slug":768,"name":768,"fn":769,"description":770,"org":771,"tags":772,"stars":782,"repoUrl":783,"updatedAt":784},"nemoclaw-user-guide","retrieve NemoClaw documentation and configuration","Guides human users' AI agents to the NemoClaw docs MCP server and canonical Fern documentation in Markdown form. Use when users ask how to install, configure, operate, troubleshoot, secure, or learn NemoClaw with an AI coding assistant. Trigger keywords - nemoclaw docs, use nemoclaw with ai agent, nemoclaw mcp docs, nemoclaw install help, nemoclaw quickstart, nemoclaw markdown docs, llms.txt, agent skills.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[773,776,779],{"name":774,"slug":775,"type":15},"Documentation","documentation",{"name":777,"slug":778,"type":15},"MCP","mcp",{"name":780,"slug":781,"type":15},"Search","search",21777,"https:\u002F\u002Fgithub.com\u002FNVIDIA\u002FNemoClaw","2026-08-25T03:29:57.273192",{"slug":786,"name":786,"fn":787,"description":788,"org":789,"tags":790,"stars":800,"repoUrl":801,"updatedAt":802},"mcore-build-and-dependency","manage Megatron-LM development environments","Container-based dev environment setup and dependency management for Megatron-LM. Covers acquiring and launching the CI container, uv package management, and updating uv.lock.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[791,794,797],{"name":792,"slug":793,"type":15},"Containers","containers",{"name":795,"slug":796,"type":15},"Deployment","deployment",{"name":798,"slug":799,"type":15},"Python","python",17049,"https:\u002F\u002Fgithub.com\u002FNVIDIA\u002FMegatron-LM","2026-07-27T06:06:11.249662",{"slug":804,"name":804,"fn":805,"description":806,"org":807,"tags":808,"stars":800,"repoUrl":801,"updatedAt":813},"mcore-bump-base-image","update NVIDIA PyTorch base images","Bump the NVIDIA PyTorch base image (`nvcr.io\u002Fnvidia\u002Fpytorch:YY.MM-py3`) used by Megatron-LM CI. Covers the two pin sites (GitHub CI in `docker\u002F.ngc_version.dev` and GitLab CI in `.gitlab\u002Fstages\u002F01.build.yml`), the post-bump CI loop (re-run functional tests, refresh golden values, mark broken tests), and the gotchas that bit PRs",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[809,812],{"name":810,"slug":811,"type":15},"CI\u002FCD","ci-cd",{"name":795,"slug":796,"type":15},"2026-07-14T05:25:59.97109",{"slug":815,"name":815,"fn":816,"description":817,"org":818,"tags":819,"stars":800,"repoUrl":801,"updatedAt":825},"mcore-cicd","manage CI\u002FCD pipelines for Megatron-LM","CI\u002FCD reference for Megatron-LM. Covers CI pipeline structure, PR scope labels, triggering internal GitLab CI (which force-pushes the current branch to a pull-request\u002FBRANCH ref — always dry-run and verify the destination first; never run against shared or protected branches), and CI failure investigation.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[820,821,822],{"name":810,"slug":811,"type":15},{"name":795,"slug":796,"type":15},{"name":823,"slug":824,"type":15},"GitHub","github","2026-08-28T14:38:16.959248",{"slug":827,"name":827,"fn":828,"description":829,"org":830,"tags":831,"stars":800,"repoUrl":801,"updatedAt":839},"mcore-create-issue","investigate CI failures and create issues","Investigate a failing GitHub Actions run or job and create a GitHub issue for the failure.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[832,835,836],{"name":833,"slug":834,"type":15},"Debugging","debugging",{"name":823,"slug":824,"type":15},{"name":837,"slug":838,"type":15},"Triage","triage","2026-07-14T05:25:57.442089",{"slug":841,"name":841,"fn":842,"description":843,"org":844,"tags":845,"stars":800,"repoUrl":801,"updatedAt":852},"mcore-linting-and-formatting","lint and format Megatron-LM code","Linting and formatting for Megatron-LM. Covers running autoformat.sh, tools (ruff, black, isort, pylint, mypy), and code style rules.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[846,849],{"name":847,"slug":848,"type":15},"Best Practices","best-practices",{"name":850,"slug":851,"type":15},"Code Analysis","code-analysis","2026-07-14T05:25:56.18433",{"slug":854,"name":854,"fn":855,"description":856,"org":857,"tags":858,"stars":800,"repoUrl":801,"updatedAt":866},"mcore-migrate-gpt-to-hybrid","migrate Megatron-LM models to HybridModel","Migration guide for moving Megatron Core GPTModel checkpoints, model providers, training commands, and layer mappings to HybridModel, including the mechanical steps for transferring an existing pretrain_gpt.py launch script.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[859,862,865],{"name":860,"slug":861,"type":15},"Machine Learning","machine-learning",{"name":863,"slug":864,"type":15},"Migration","migration",{"name":9,"slug":8,"type":15},"2026-08-21T03:36:57.470256",{"slug":868,"name":868,"fn":869,"description":870,"org":871,"tags":872,"stars":800,"repoUrl":801,"updatedAt":879},"mcore-onboard-gb200-1node-tests","onboard functional tests for GB200","Onboard 1-node GitHub MR functional tests for GB200 from existing mr-scoped 2-node tests.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[873,876],{"name":874,"slug":875,"type":15},"QA","qa",{"name":877,"slug":878,"type":15},"Testing","testing","2026-07-14T05:25:53.673039",{"slug":881,"name":881,"fn":882,"description":883,"org":884,"tags":885,"stars":800,"repoUrl":801,"updatedAt":890},"mcore-run-on-slurm","launch distributed training jobs on SLURM","How to launch distributed Megatron-LM training jobs on a SLURM cluster. Covers a minimal sbatch skeleton, environment-variable setup for torch.distributed.run, CUDA_DEVICE_MAX_CONNECTIONS rules across hardware and parallelism modes, container conventions, monitoring, and per-rank failure diagnosis.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[886,887],{"name":795,"slug":796,"type":15},{"name":888,"slug":889,"type":15},"Infrastructure","infrastructure","2026-07-14T05:25:49.362534",{"slug":892,"name":892,"fn":893,"description":894,"org":895,"tags":896,"stars":800,"repoUrl":801,"updatedAt":904},"mcore-split-pr","split pull requests to reduce review load","Split a PR into multiple PRs to reduce the number of required CODEOWNERS reviewer groups.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[897,900,901],{"name":898,"slug":899,"type":15},"Code Review","code-review",{"name":823,"slug":824,"type":15},{"name":902,"slug":903,"type":15},"Pull Requests","pull-requests","2026-08-25T03:29:16.211287",{"slug":906,"name":906,"fn":907,"description":908,"org":909,"tags":910,"stars":800,"repoUrl":801,"updatedAt":913},"mcore-testing","run and manage Megatron-LM tests","Test system for Megatron-LM. Covers test layout, recipe YAML structure, adding and running unit and functional tests, golden values, marker filters, and CI parity.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[911,912],{"name":874,"slug":875,"type":15},{"name":877,"slug":878,"type":15},"2026-07-14T05:25:54.928983",{"slug":915,"name":915,"fn":916,"description":917,"org":918,"tags":919,"stars":800,"repoUrl":801,"updatedAt":924},"nightly-sync","manage nightly main-to-dev sync workflows","Domain knowledge for the nightly main-to-dev sync workflow. Covers merge strategy, CI architecture, failure investigation, and known issues.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[920,923],{"name":921,"slug":922,"type":15},"Automation","automation",{"name":810,"slug":811,"type":15},"2026-07-30T05:29:03.275638",563,{"items":927,"total":945},[928,940],{"slug":929,"name":929,"fn":5,"description":930,"org":931,"tags":932,"stars":19,"repoUrl":20,"updatedAt":939},"rest-core-grpc-proxy","Build or migrate infra-controller REST API endpoints that call on-site NICo Core through the generic Core gRPC proxy. Use when working on REST-to-Core operations, ExecuteCoreGRPC, grpcproxy, forge.Forge methods, creating new proxied REST endpoints, or migrating bespoke workflows to the gRPC proxy.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[933,934,937,938],{"name":17,"slug":18,"type":15},{"name":935,"slug":936,"type":15},"Engineering","engineering",{"name":9,"slug":8,"type":15},{"name":13,"slug":14,"type":15},"2026-08-12T04:27:01.311843",{"slug":4,"name":4,"fn":5,"description":6,"org":941,"tags":942,"stars":19,"repoUrl":20,"updatedAt":21},{"slug":8,"name":9,"logoUrl":10,"githubOrg":9},[943,944],{"name":17,"slug":18,"type":15},{"name":13,"slug":14,"type":15},2]