[{"content":" This is my current agentic development workflow in May 2026.\nThe short version: I run almost everything through Codex now. Not because every other tool is bad. The whole space is moving extremely fast, and a lot of tools are interesting. But for the work I actually do every day, the current GPT model in Codex is the best fit.\nThe longer version is more about the loop than the model.\nThe model matters. A lot. But the workflow around it matters just as much: repo instructions, small helper scripts, local skills, review passes, docs discovery, commit discipline, and a way to make the agent prove what it changed.\nThat is the part that changed the most for me.\nWhere I Started For me this started with the first public version of ChatGPT. As soon as it was available, I was using it wherever it helped.\nAt first I used it mostly as an explainer and learning tool. I still do. It was useful for understanding an unfamiliar API, asking why something worked, or getting a second explanation when the docs did not quite click.\nPretty quickly, I also saw the power for coding. Not as something to blindly copy from, but as an aid in the process. Copy a piece of code in. Ask for a fix. Paste the error back. Ask again. It was useful, but it was not really a developer sitting in the repo.\nI was also using GitHub Copilot: useful completions, useful model swaps now and then, and still a subscription I keep around. It can be handy with OpenCode when I want to try another model path. But I rarely use it these days.\nThe first version that felt more agentic came at the end of 2023 and beginning of 2024, through the VS Code extension era: Roo Code, Cline-style workflows, and tools that could read files, edit files, run commands, and ask for approval along the way.\nThat was a big shift.\nIt is history for me now. I have not used Roo Code for about a year, but that period mattered because it showed what the workflow could become.\nIt also created a lot of bloat.\nThe agent could do more, so it did more. More files. More plans. More scaffolding. More \u0026ldquo;helpful\u0026rdquo; abstractions. The result was often impressive in the moment and annoying the next day. You got motion, but not always good taste.\nThen Claude Code changed the shape of the workflow again.\nTerminal-first mattered. Better tool use mattered. Letting the agent live closer to the repo instead of inside a chat box mattered. It still needed supervision, but it felt like a real step toward agentic development instead of prompt-and-paste development.\nBoris Cherny\u0026rsquo;s way of talking about Claude Code also influenced me in that early phase. The useful idea was not \u0026ldquo;let an agent do anything\u0026rdquo;. It was more specific: keep the agent close to the terminal, make it search and inspect before it edits, and treat the workflow as a real engineering loop instead of a smarter autocomplete box.\nI also used OpenCode with oh-my-opencode. That was much better than the early extension loops in a lot of ways. More structured. More configurable. More serious about agents as a harness.\nBut my workflow was still too often:\nflowchart LR Plan[Plan] --\u003e Execute[Execute] Execute --\u003e Inspect[Inspect] Inspect --\u003e Replan[Ask for another plan] Replan --\u003e ExecuteAgain[Execute again] That can work. But it is slow, and it encourages the agent to spend too much energy narrating the work instead of closing the loop.\nThe models improved. The harnesses improved. The results improved. But the real unlock for me was reducing the distance between task, edit, test, review, and commit.\nWhy I Moved Fully To Codex This is intentionally a May 2026 snapshot, not a timeless model ranking.\nAt the beginning of the year, I was still using Claude every day. Opus 4.5 and Opus 4.6 were good for this kind of work. They had strong taste, strong language, and they were often excellent at turning a messy request into a plausible implementation plan.\nThe move was not because Claude suddenly became bad. It was because my workflow changed. I wanted less plan negotiation and more closed-loop repo work: read the rules, inspect the code, make the smallest useful patch, run the checks, review the patch, fix the real findings, and hand back proof.\nI moved fully to Codex when GPT-5.3-Codex was there.\nThat was the first point where Codex clearly fit my daily coding work better than Claude Opus 4.6. Opus was still very good, but it could be trigger-happy. It liked to do a lot. Sometimes too much. Codex needed better guardrails, but once those guardrails were there, it just got the job done more reliably.\nThe workflow also felt more natural to me. I could talk to the agent, let it search, let it build up the intent and context, and then let it implement. Less giant planning ceremony. More discovery into action. Fewer moments where I felt like I was managing the assistant instead of delegating work to it.\nWhat I did not like was losing some of the personality of Opus. It was more fun to work with. It had a nicer tone. But that is not the main reason I am here. I need to build.\nWith GPT-5.5, the quality jump is big again. The same workflow works better than before: fewer misses, better judgment, better use of instructions, and less need to constantly steer the model back onto the rails.\nAt the same time, Claude moved in a direction I like less for agentic coding. Opus 4.7 did not reverse that for me. In some ways it felt worse before it felt better. The giant context window sounds attractive, but in practice I do not want the workflow to depend on one specific Claude setup, or on manually picking the special context mode in Claude\u0026rsquo;s own CLI. I want the model to work well inside the harness I choose.\nI also do not love the ecosystem gravity around CLAUDE.md and Claude-specific conventions while the rest of the industry is slowly moving toward more shared agent instructions. The leaked-code discussion around Claude Code also reinforced my feeling that the harness itself was not the magic. I did not read it as \u0026ldquo;Claude Code is bad\u0026rdquo;. I read it as another reminder that the wrapper layer matters, and that I want more of that layer open, inspectable, and portable.\nThat is the broader issue for me: Anthropic often makes the experience pleasant, especially if you are not technical, but it also hides too much from the user. For technical work, I want more control, more portability, and more visibility into the harness. The Claude CLI never felt like the best version of that.\nSo for coding, right now, Codex with GPT-5.5 is my default. I am fully in Codex and not really looking back, at least for now.\nThat does not mean I never use anything else. For some web design work I still sometimes reach for Claude Opus, especially when I want a different taste pass. Smaller and cheaper models are also worth watching: GLM, Kimi, and the rest of that ecosystem are getting better quickly.\nThe reason is not just \u0026ldquo;it writes better code\u0026rdquo;.\nIt follows the repo. It uses tools well. It handles boring constraints. It can keep a large task in its head without trying to turn everything into a framework. And when the instructions are good, it can work in a way that feels less like prompting and more like delegating.\nThat is the bar.\nI do not want an agent that only produces code. I want an agent that can:\nread the repo rules find the relevant docs make a narrow change run the right checks review its own patch fix what the review finds commit only the intended files tell me what is still uncertain The model is important because it makes that whole loop less fragile.\nThe Base Layer: Agent Scripts My shared base layer is agent-scripts, my fork of steipete/agent-scripts.\nIt is not glamorous. That is why it works.\nPeter Steinberger influenced this part of my workflow a lot. His agent scripts, skills, review habits, and general bias toward running many small agent workflows in parallel helped me stop thinking of this as one heroic chat session and start treating it as an operating layer around the repo.\nThere are three parts I care about most:\nshared instructions repo docs discovery small helpers that remove bad defaults AGENTS.MD agent-scripts gives my repos a common set of instructions and small tools. The important file is AGENTS.MD: the shared rules that tell agents how I want them to work.\nThings like:\nread repo docs before coding do not reset my worktree do not swap package managers use repo-local commands add tests when the bug shape fits run the full gate before handoff keep diffs narrow commit with the helper do not print secrets ask before doing risky infrastructure work That sounds like process. It is really context.\nAgents are much better when the rules are not hidden in my head. The goal is to remove \u0026ldquo;Bram would have wanted\u0026hellip;\u0026rdquo; guessing from every session.\nEach repo can still add local rules. This blog repo, for example, says to read the shared AGENTS.MD, then run bin/docs-list, then read the matching docs by read_when. That is exactly the kind of small, boring instruction that saves time. The agent does not need to search the whole repo to understand how posts work. It asks the docs inventory first.\ndocs-list docs-list is one of those tiny helpers that sounds too simple to matter until you use it every day.\nThe pattern is:\ndocs/*.md summary: ... read_when: - ... Then the agent runs:\nbin/docs-list and gets a small map of the repo.\nNot a giant context dump. Not a wiki search. Just enough to know which doc matters for the current task.\nFor this post, the relevant doc is the content workflow. It says new posts start in Obsidian, under:\n/Users/bram/obsidian/bvrA/posts Then the repo syncs them into Hugo with:\nbin/sync-obsidian-posts That is the kind of thing I want the model to discover and follow without me repeating it every time.\ncommitter I use a small committer helper from agent-scripts.\nThe important behavior is simple: start from a clean staging area, stage only the files I list, then commit with the message I give it.\nThat matters because agent sessions often leave noise nearby: generated files, local notes, experiments, unrelated user changes, maybe a synced artifact. I do not want \u0026ldquo;git add .\u0026rdquo; as a habit.\nThe helper enforces that habit. It rejects . as a path, checks that the listed files exist or are real deletions, unstages everything first, stages only the explicit paths, and refuses to commit if nothing changed.\nThe commit step should be boring and explicit:\ncommitter \u0026#34;fix: handle empty export path\u0026#34; src/exporter.py tests/test_exporter.py That is also a good forcing function for the agent. If it cannot name the files it wants to commit, it probably does not understand the patch well enough.\nSkills The other big piece is local skills.\nSkills are small operational playbooks. They are not long essays. They tell the agent what to do when a certain kind of task appears.\nIn my setup, a skill has a SKILL.md with front matter:\n--- name: codex-review description: \u0026#34;Codex code review closeout: local dirty changes, PR branch vs main, parallel tests.\u0026#34; --- The description is for routing. The body is for execution.\nThat has been surprisingly powerful.\nWhat Belongs In Skills The useful skills are the ones that capture a repeatable workflow I do not want the model to rediscover.\nIn my setup that includes:\ncode review closeout Oracle second-model review Obsidian note work GitHub deep review 1Password rules npm release work frontend design screenshots and UI inspection remote host workflows The point is not to turn everything into a skill. The point is to capture the workflows where I already know the rules.\nDo not make the agent rediscover the 1Password policy. Do not make it guess how npm release verification works. Do not make it remember the exact review loop. Put that into a skill and let the model pick it up when the task matches.\nReview Loop The review loop has been the biggest recent gamechanger.\nThe old version was:\nflowchart LR AgentEdits[Agent edits] --\u003e Inspect[I inspect] Inspect --\u003e AskFixes[I ask for fixes] AskFixes --\u003e AgentEditsAgain[Agent edits again] The better version is:\nflowchart LR AgentEdits[Agent edits] --\u003e Tests[Tests] Tests --\u003e Review[Codex review] Review --\u003e Findings{Accepted fixes?} Findings -- yes --\u003e Fixes[Fix real issues] Fixes --\u003e Tests Findings -- no --\u003e Clean[Review clean] Codex-review makes that explicit.\nThis is one of the most powerful pieces of the whole setup, and I use it daily now.\nIt treats review output as advisory, not sacred. The agent still has to verify each finding against the real code path, adjacent files, and dependency behavior when needed. Bad findings get rejected. Speculative rewrites get rejected. Real findings get fixed at the smallest useful ownership boundary. If a review-triggered fix changes code, the focused tests run again and review runs again.\nThe important bit is that this closes the loop before the work gets back to me.\nI still review the diff. I still make the call. But I am no longer the first person to notice the obvious bug the agent introduced ten minutes ago.\nFor non-trivial changes, this is now part of my default expectation:\ncodex review --uncommitted or, on a branch:\ncodex review --base origin/main Sometimes the review finds nothing. Sometimes it catches exactly the kind of edge case that would have been annoying in production. Either way, the discipline matters.\nI also wrapped that in a helper:\ncodex-review --parallel-tests \u0026#34;go test ./...\u0026#34; The helper chooses the right review target. If the checkout is dirty, it runs codex review --uncommitted. If the work is already committed or on a PR branch, it reviews against the branch base instead, usually origin/main or the actual PR base. That matters because a clean --uncommitted review only proves there is no local patch. It says nothing about a branch that is already committed.\nIt can run tests and review in parallel, captures the review output, fails if Codex reports P0-P3 findings, treats empty review output as non-clean, and prints a clean closeout only when there are no accepted/actionable findings:\ncodex-review clean: no accepted/actionable findings reported The point is not to obey a second model. The point is to add another pressure test to the patch before I see it. The agent has to read the finding, decide whether it is real, fix it if it is real, reject it if it is not, and keep looping until the review is clean.\nOracle Oracle used to be more central in my workflow.\nThe idea is good: bundle a prompt plus selected files and hand it to another model as a second opinion. I found it very useful, especially when I wanted a separate pass on architecture, debugging, or a hard refactor.\nIn my setup it lives as a skill and a CLI:\nnpx -y @steipete/oracle --help These days I need it less.\nThat is mostly because GPT-5.5 in Codex is already strong enough for a lot of the work where I previously wanted a second model. The better the primary loop gets, the less often I need to stop and ask a different model to reason from a bundle.\nBut Oracle still has a place.\nFor really hard tasks, I still like having another model look at the problem with a tight file set and a standalone prompt. It is especially useful when I want to separate \u0026ldquo;implement this\u0026rdquo; from \u0026ldquo;tell me what I am missing\u0026rdquo;.\nThe trick is not to overuse it. A second opinion is useful when the task is hard. It is drag when the primary loop can solve the problem faster.\nGoogle Models Google models have their spot. I just do not currently reach for them first for code development.\nAt the end of last year, Google models were often the best fit for UI design work in my setup. They had a good feel for layout, visual hierarchy, and turning a rough interface idea into something that looked more intentional.\nFor coding, they were less compelling for me. Not useless. Just less optimized for the loop I care about. They did not expose or maintain the same kind of thinking-and-correction rhythm, and they did not correct themselves after failed checks as often as I wanted.\nI still think Google\u0026rsquo;s DESIGN.md spec is a good idea: a plain Markdown design-system file with structured tokens and human-readable design rationale that coding agents can follow.\nThat is a nice idea.\nIt fits a broader pattern I like: put the important context in files, make it readable by humans, and make it precise enough for agents.\nBut I think Google is playing a different game. World models, robotics, multimodal systems, big context, and other things that are not just \u0026ldquo;make my repo patch better today\u0026rdquo;.\nThat is not a criticism. It is just a different center of gravity.\nFor coding, in my current day-to-day work, GPT-5.5 in Codex is the tool I trust most.\nSmaller Models And Other CLIs The cheap and fast models are worth paying attention to.\nGLM 5 was nice to use for a while. Kimi too. Cheap, fast, and good enough for certain slices of work: summarizing, searching, mechanical edits, draft review, simple scripts, and background exploration.\nThen Opus 4.6 and GPT-5.2-Codex were good enough that I stopped caring as much about cheaper routing in my own workflow. The cost difference mattered less than the cost of reviewing weaker output.\nNow even the small GPT-5.5 models are cheap relative to what they can do. I am not using that layer heavily right now, but I am watching it. The useful split may come back: frontier model for final patch judgment, smaller models for background exploration and cheap parallel passes.\nThe same is true for the CLI ecosystem.\nCodex, Claude Code, OpenCode, Gemini CLI, and all the wrappers around them are not just interchangeable chat frontends. They encode different opinions:\nplanning versus direct execution terminal versus editor one agent versus subagents MCP-heavy versus CLI-heavy local files versus cloud sessions cheap model routing versus frontier model first This world is changing fast.\nThat is why I like keeping my own workflow in small files and scripts. If the best harness changes, I can move the layer above it. AGENTS.MD, skills, docs-list, and commit discipline are portable ideas.\nThe Current Loop My current loop looks roughly like this:\nIt still often starts with just talking to the model.\nI usually know what I have in mind. I guide the model toward that shape, but I also want it to push back, surface missing information, or suggest a path I had not considered. That conversational part matters. Just talking to the model can get you surprisingly far, especially when you use it to clarify the task before touching files.\nOnce I have enough information, and the model has enough context, then I want it to switch into implementation mode.\nThis is the main proof shape of the workflow: conversational discovery first, then a set of gates that force repo context, checks, review, and explicit handoff.\nflowchart TD Task[Task] --\u003e Conversation[Talk through intent] Conversation --\u003e Clarify[Surface gaps and options] Clarify --\u003e Ready{Enough context?} Ready -- no --\u003e Conversation Ready -- yes --\u003e Rules[Read AGENTS.MD] Rules --\u003e Docs[Run docs-list] Docs --\u003e Context[Read matching docs] Context --\u003e Inspect[Inspect the repo] Inspect --\u003e Edit[Make a narrow edit] Edit --\u003e Checks[Run focused checks] Checks --\u003e Review[Run codex review] Review --\u003e Findings{Accepted findings?} Findings -- yes --\u003e Fix[Fix real issues] Fix --\u003e Checks Findings -- no --\u003e Gate[Run final gate] Gate --\u003e Commit[Commit explicit files] Commit --\u003e Handoff[Handoff with proof and uncertainty] For app work, there may be screenshots or Playwright checks.\nFor Maya work, there may be a live Maya session involved.\nFor release work, there are changelog and registry checks.\nThe shape changes per repo. The principle stays the same:\nThe agent should close the loop as much as it reasonably can before handing work back.\nA Real Example: gobankcli gobankcli is a good example because the problem was boring in exactly the right way: build a local-first, read-only bank transaction archive without letting an agent make unsafe assumptions.\nIt took me a few hours to create end to end. That is the part that still feels different: not a weekend of scaffolding, not a week of wiring, but a focused session where the agent could keep moving because the constraints were clear.\nThe first commit was not code. It was AGENTS.md.\nThat file set the boundaries before implementation started:\nno scraping no payment initiation no bank password storage no real bank data in tests, docs, examples, logs, or commits no float64 for money stdout is data only; stderr is hints and progress --json, --plain, and --no-input must be scriptable Then the repo grew as a stack of small commits:\n2026-05-17 2817f85 chore: add agent instructions 2026-05-17 65e9d05 feat: add provider abstraction 2026-05-17 d802a51 feat: add sqlite archive store 2026-05-17 a9fc856 feat: add normalized csv export 2026-05-17 15f8f0c feat: add gocardless provider normalization 2026-05-17 47c4b81 feat: wire provider commands 2026-05-17 61e7376 feat: add read-only archive query command 2026-05-17 7a0d51c fix: handle provider sync edge cases 2026-05-17 eb80840 fix: keep remittance out of transaction identity 2026-05-18 d433841 feat: add enable banking local HTTPS setup That sequence matters more than the size of any one patch. The agent did not start by inventing a giant banking framework. It started with repo rules, then added the provider boundary, archive store, export path, provider normalization, command wiring, query surface, and docs.\nThe fix commits are the useful part. fix: handle provider sync edge cases added command and normalization coverage around real provider-shape problems. fix: keep remittance out of transaction identity is exactly the kind of bug I want this workflow to catch: one provider field may be useful descriptive data, but it should not accidentally become part of a stable transaction identity.\nThe proof is not that the agent wrote a lot of Go. The proof is that the work stayed inside the constraints: read-only, local-first, synthetic fixtures only, explicit command behavior, tests beside behavior changes, and commits narrow enough that I could inspect the shape of the system afterwards.\nWhat I Want From Agents Now I do not want maximal autonomy. I want useful autonomy.\nThat means:\nknow the repo rules keep changes small use the right local tools prove the important behavior review the patch say what was not verified do not hide uncertainty The best agentic workflow is not the one with the most moving parts. It is the one where the model has enough context and enough tools to finish the boring parts without turning the repo into a mess.\nRight now, for me, that is Codex plus agent-scripts, local skills, docs discovery, review loops, and explicit commits.\nNo mysticism. Just a tighter loop.\n","permalink":"https://articles.bramvanrompuy.be/posts/agentic-workflow-may-2026/","summary":"How I run agentic development in May 2026: shared repo instructions, local skills, tight review loops, and Codex as the main daily driver.","title":"My Agentic Workflow, May 2026"},{"content":" This is my Codex workflow for Autodesk Maya.\nThe short version: I do not want an agent that only edits Maya Python files. I want it to drive Maya, run the tool in the actual host application, inspect what happened, patch the code, and run it again.\nThat sounds obvious. It is not how most coding agents work by default.\nMost agents are still file-system shaped. They read files, edit files, run tests, and report a diff. That works well for a CLI or a web app. It is only half the loop for Maya.\nMaya is the runtime. The scene is state. The UI matters. Plugin load order matters. Script editor errors matter. A tool can import fine in plain Python and still fail the moment Maya\u0026rsquo;s event loop, command engine, or scene graph gets involved.\nSo I built the workflow around a small bridge to a real Maya session.\nNot because I want more agent infrastructure. I want less guessing.\nThe Problem I do a lot of Autodesk Maya work for clients: rigging, pipeline tools, exporters, scene automation, UI work, and all the annoying little helpers that only make sense inside a DCC application.\nThis work has a nasty property: the source code lies.\nA Python function can look correct. The unit test can pass. The package can import. Then Maya loads it in a different environment, with a different plugin state, against a real production scene, and suddenly the thing falls apart.\nExamples:\na UI works until it is reopened after a reload a rig helper creates nodes with the wrong namespace a tool assumes a plugin is loaded a menu command survives import but fails when triggered from Maya a script works on a clean scene but not on the client scene a callback keeps state from the previous run This is not exotic. This is normal Maya development.\nIf Codex only sees the repo, it can produce plausible patches. Sometimes very good patches. But it is still guessing about the thing that actually matters: what happens in Maya.\nThe Manual Loop The old workflow was useful, but too much of the loop lived in my hands.\nflowchart LR Ask[Ask for code] --\u003e Copy[Copy or sync into tool] Copy --\u003e Reload[Reload Maya or package] Reload --\u003e Click[Click around in Maya] Click --\u003e Error[Copy host error back] Error --\u003e Patch[Ask for another patch] Patch --\u003e Copy That works, but it wastes the human on glue work.\nThe agent can write code, but I am still the runtime adapter. I am the one reloading the tool, triggering the menu command, copying the script editor output, and telling the model what really happened.\nFor Maya, that is the wrong split.\nThe Bridge The core is a small tool I call gg_mayasessiond. Its job is to keep Maya reachable from Codex without turning the whole application into a free-for-all.\nThe idea is boring on purpose:\nkeep Maya running expose a small set of commands let Codex attach to that session run focused checks inside Maya send back useful errors I do not want Codex to have \u0026ldquo;access to Maya\u0026rdquo; in some vague magical sense. I want it to have a few simple operations I can trust.\nStart Maya. Load the tool. Run a small check. Inspect the scene. Read the errors. Report what happened.\nThe command surface is intentionally small: check the environment, start the session, ask for status, call a tool, read a short report, stop the session. Behind that it keeps a state folder with the current process state, recent events, and the daemon log. That sounds mundane, but it is exactly what makes the workflow usable. When something fails, Codex can point at the log instead of guessing.\nCalls go through the long-running Maya session first. If that path is unhealthy, the tooling can fall back to a one-shot call. The important thing is that the result says which path was used, so I can tell whether a check really touched the live session.\nThat is enough.\nThe Better Loop The loop I want is not \u0026ldquo;agent writes code, human tests it later\u0026rdquo;.\nIt is a closed loop around the real host app.\nflowchart TD Task[Task] --\u003e Rules[Read AGENTS.md] Rules --\u003e Docs[Read matching repo docs] Docs --\u003e Inspect[Inspect Maya tool code] Inspect --\u003e Session[Start or attach to Maya] Session --\u003e Probe[Run tiny Maya-side probe] Probe --\u003e Edit[Make narrow code change] Edit --\u003e Reload[Reload or rerun in Maya] Reload --\u003e Result{Host result clean?} Result -- no --\u003e Error[Read host error and logs] Error --\u003e Edit Result -- yes --\u003e Review[Review diff and evidence] Review --\u003e Handoff[Report files, command, result, uncertainty] The important part is the rerun in Maya.\nWithout it, the agent is doing what every developer does in a new Maya codebase: reading code and making educated guesses. Useful, but incomplete.\nWith it, Codex can close the loop in the host application. That is the difference between \u0026ldquo;AI generated a patch\u0026rdquo; and \u0026ldquo;the tool actually ran in Maya\u0026rdquo;.\nTests Still Matter Tests are great. I still want tests.\nBut a lot of Maya bugs are integration bugs with a very expensive runtime attached. Mocking Maya too aggressively gives you confidence in the mock. Running plain Python catches syntax and some logic. It does not tell you whether the real command behaves correctly in the real application.\nThe useful split is:\nflowchart LR Logic[Plain Python tests] --\u003e Confidence[Confidence] Static[Static checks and imports] --\u003e Confidence Host[Maya session checks] --\u003e Confidence Confidence --\u003e Proof[Useful proof] For normal repo work, Codex can still do the usual things:\ngit status --short rg --files pytest For Maya work, those are setup steps. The proof is inside Maya.\nMCP Is Useful Here, But Keep It Small I am not generally excited by giant MCP servers that dump half the world into the agent context. They often feel like a tax. Lots of tokens, lots of vague affordances, not much signal.\nMaya is one of the places where a narrow tool bridge actually makes sense.\nMaya is huge. You do not want an agent wandering around it with unlimited freedom. You want a small surface that exposes the few things needed for a development loop:\ncurrent session state known commands explicit inputs and outputs recent host errors scene inspection helpers reload hooks smoke checks That can be MCP. It can also be a CLI. I do not care much about the wrapper. I care that the interface is small and easy for the agent to call.\nThe best tool boundary is boring. If I cannot explain the operation in one sentence, it probably does not belong in the first version.\nRepo Instructions Still Matter The Maya bridge is the interesting part. The boring part is what keeps it usable.\nEvery serious repo needs an AGENTS.md.\nThat file tells Codex what the repo is, what to read, which commands are safe, how to test, what not to touch, and what counts as done. For Maya work, it also needs to say when the Maya session is part of verification.\nMy shared defaults live in ~/Projects/agent-scripts. That gives repos a common base: git rules, docs conventions, helper scripts, and small workflow notes. Project repos then add their local rules.\nFor Maya, the useful instructions are concrete:\nread the repo docs before editing keep diffs narrow do not reset my worktree do not swap package managers prefer repo scripts use the Maya session when available say which Maya-side command was run include host errors in the final report stop if the session state is unclear This is not bureaucracy. This is how you stop automation from turning into an expensive randomizer.\nSafety Boundary This needs discipline.\nMaya scenes can contain client work. Tools can mutate files, shelves, preferences, scenes, and caches. An agent should not get broad write access just because it can run a command.\nThe bridge needs clear boundaries:\nflowchart TD Agent[Codex] --\u003e Bridge[Small Maya bridge] Bridge --\u003e Allowed[Allowed commands] Bridge --\u003e State[Session state] Bridge --\u003e Logs[Recent host errors] Allowed --\u003e Maya[Maya session] State --\u003e Proof[Verification report] Logs --\u003e Proof Maya --\u003e Proof Client[Client scenes and secrets] -. stay outside .- Bridge The agent should say:\ncommand run result relevant error files changed confidence what it did not verify Not a victory lap.\nI also do not want client-specific details leaking into logs, blog posts, or public issues. Verification reports need to be useful without being chatty.\nWhat This Changes The better loop lets me delegate tasks that used to require me to sit in the middle:\nreproduce a tool failure test a menu command inspect generated nodes validate a rig helper run a scene migration smoke check capture the exact host error verify that a reload actually fixed the issue That last point matters. A lot of Maya bugs are reload bugs. If the agent cannot reload the tool in Maya, it cannot really verify them.\nThis is also why the workflow fits the broader agentic setup I use now: repo instructions, docs discovery, small skills, focused checks, review loops, and explicit handoff. The Maya bridge is not a separate philosophy. It is the same loop pointed at a host application.\nWhy I Like This Direction The point is not \u0026ldquo;AI for Maya\u0026rdquo;.\nThe point is that serious software development often happens inside environments that are not nice little test runners. Maya, Unity, Unreal, Houdini, Blender, CAD tools, internal pipeline apps, weird vendor SDKs. The code is only part of the system.\nAgents get much more useful when they can touch the real runtime through a narrow, repeatable, auditable interface.\nThat is what the Maya bridge gives me.\nIt turns Codex from a code patcher into something closer to a developer sitting next to the host app. Still supervised. Still constrained. But finally looking at the thing I care about.\nNext The next step is making the Maya loop boring enough that I trust it every day.\nI want repeatable recipes for:\nstarting a clean Maya session attaching to an existing session loading the tool running a small check reloading after a patch capturing the host errors Once that is solid, the workflow becomes obvious:\nGive Codex the repo, the rules, and the smallest possible bridge into Maya. Let it work. Review the evidence.\nNo mysticism. Just a tighter loop.\n","permalink":"https://articles.bramvanrompuy.be/posts/codex-workflows-agentic-development/","summary":"A practical Codex workflow for Autodesk Maya: one real host app, one small bridge, and fewer guesses.","title":"Serving Autodesk Maya to Codex"},{"content":"Introduction Building a website doesn\u0026rsquo;t have to be complex. This guide simplifies the process, showing you how to set up a Hugo website with Docker, manage content in Obsidian using Markdown files, and deploy via Cloudflare. This combination gives you a fast local writing workflow and a clean path from draft to live site.\nTool Overview Hugo: A powerful static site generator that transforms Markdown into a full HTML website, renowned for its speed and simplicity. Obsidian: A Markdown note app that works well for drafting, editing metadata, and organizing posts in a local vault. Docker: A tool that packages applications into containers, ensuring consistent environments across development, testing, and production stages. Cloudflare: Provides a secure, fast, and reliable global network, with Cloudflare Pages offering an effortless static site deployment solution. Prerequisites Before starting, ensure you have the following tools installed and accounts set up:\nDocker: Install from Docker\u0026rsquo;s official site. Git: Installation guide available at Git\u0026rsquo;s official book. Alternatively, for a GUI approach, GitHub Desktop. Obsidian: Install Obsidian and create or open a local vault for your writing. Cloudflare Account: Sign up at Cloudflare\u0026rsquo;s website and follow the steps to add your domain or choose a provided .dev domain. Docker and Hugo Setup Quickstart with Docker Creating a Hugo site with Docker ensures a consistent development environment. Use the floryn90/hugo Docker image for a minimal setup that includes all necessary features.\nCreate a New Hugo Site: Execute the following command, replacing placeholders with your details:\ndocker run --rm -v \u0026#34;\u0026lt;path-to-your-projects-directory\u0026gt;:/src\u0026#34; floryn90/hugo:0.123.4-ext-alpine new site \u0026lt;your-site-name\u0026gt; --format yaml Run the Hugo Server: Start the server to view your site locally at http://localhost:1313:\ndocker run --rm -p 1313:1313 -v \u0026#34;\u0026lt;path-to-your-projects-directory\u0026gt;:/src\u0026#34; floryn90/hugo:0.123.4-ext-alpine server Theme Installation: PaperMod PaperMod is a high-performance Hugo theme. Install it for a visually appealing and functional site layout.\nInitialize a Git Repository in your site\u0026rsquo;s root directory: git init Clone the PaperMod Theme: git clone https://github.com/adityatelange/hugo-PaperMod themes/PaperMod --depth=1 Add the Theme to Git: git add themes/PaperMod Configuring Your Hugo Site Adjust hugo.yaml to define your site\u0026rsquo;s global settings, like baseURL, language, theme, and more. Tailor the configuration to enhance site development and integration with various services.\nBasic Configuration: Set your site\u0026rsquo;s URL, language, title, and theme. Customize further for inline shortcodes, SEO, and syntax highlighting.\nAdvanced Settings: Configure languages, site parameters, output formats, and privacy settings according to your preferences and needs.\nExample Hugo Configuration # Basic Configuration baseURL: https://example.org/ # The root URL where your site is accessible. languageCode: en-us # Default language, here set to American English. title: My New Hugo Site # The global title of your site. theme: \u0026#34;PaperMod\u0026#34; # The theme your site uses, \u0026#34;PaperMod\u0026#34; in this case. # Features and Behavior enableInlineShortcodes: true # Allows using Hugo\u0026#39;s inline shortcodes. enableRobotsTXT: true # Generates a robots.txt file for search engine instructions. buildDrafts: false # Controls whether drafts are built. buildFuture: false # Controls whether future-dated content is built. buildExpired: false # Controls whether expired content is built. enableEmoji: true # Converts emoji shorthand in content to actual emojis. pygmentsUseClasses: true # Use CSS classes for syntax highlighting styles. googleAnalytics: G-Code-Here # Google Analytics tracking ID. # Languages Configuration languages: en: languageName: \u0026#34;English\u0026#34; # Name of the language. weight: 1 # Priority of the language. taxonomies: # Defines taxonomies for the site. category: categories tag: tags series: series menu: # Defines the main menu items. main: - name: Archive url: archives weight: 5 - name: Search url: search/ weight: 10 - name: Tags url: tags/ weight: 10 - name: Portfolio url: https://bramvanrompuy.be # Site Parameters params: defaultTheme: auto # Theme mode (auto, light, dark). env: production # Environment setting. title: BVR Articles # Site title for display. ShowShareButtons: true # Toggle share buttons. ShowReadingTime: true # Display reading time. displayFullLangName: true # Show full language name. ShowPostNavLinks: true # Navigation links between posts. ShowBreadCrumbs: true # Breadcrumb navigation. ShowCodeCopyButtons: true # Copy code button in code blocks. ShowRssButtonInSectionTermList: true # RSS button in sections. ShowToc: true # Table of Contents on pages. tocopen: true # TOC starts open. comments: true # Enable comments. description: \u0026#34;A place where you can find tech related articles...\u0026#34; # Site description. author: Bram Van Rompuy # Author name. giscusTheme: preferred_color_scheme # Theme for Giscus comments. disableComments: false # Toggle comments globally. images: [\u0026#34;papermod-cover.png\u0026#34;] # Default site images. homeInfoParams: # Custom parameters for the homepage. Title: Hi there! Content: Welcome to my little corner of the web... socialIcons: # Social media icons and links. - name: email url: \u0026#34;mailto:bram.van.rompuy@gmail.com\u0026#34; - name: linkedin url: \u0026#34;https://www.linkedin.com/in/bram-van-rompuy/\u0026#34; - name: github url: \u0026#34;https://github.com/BramVR\u0026#34; # Output Formats outputs: home: - HTML - RSS - JSON # Essential for certain themes or features. # Content Rendering Options markup: goldmark: renderer: unsafe: true # Allows HTML in Markdown files. highlight: noClasses: false # Use classes for syntax highlighting. # Privacy and Third-party Services privacy: vimeo: disabled: false # Toggle Vimeo embeds. simple: true # Use a simple privacy-enhanced embed. twitter: disabled: false # Toggle Twitter embeds. enableDNT: true # Enable \u0026#34;Do Not Track\u0026#34;. simple: true # Use a simple embed. instagram: disabled: false # Toggle Instagram embeds. simple: true # Use a simple embed. youtube: disabled: false # Toggle YouTube embeds. privacyEnhanced: true # Use privacy-enhanced mode. services: instagram: disableInlineCSS: true # Disable inline CSS for Instagram embeds. twitter: disableInlineCSS: true # Disable inline CSS for Twitter embeds. Enhanced Development with Docker Compose For live reloading and a smoother development experience, use Docker Compose.\nSetup Docker Compose: Create a docker-compose.yml in your Hugo project\u0026rsquo;s root with the necessary configuration for the Hugo server and live reloading.\nversion: \u0026#39;3.8\u0026#39; services: server: image: floryn90/hugo:0.123.4-ext-alpine command: server -D --poll 700ms volumes: - \u0026#34;.:/src\u0026#34; ports: - \u0026#34;1313:1313\u0026#34; This configuration defines a single service called server that:\nUses the floryn90/hugo:0.123.4-ext-alpine image. Executes the hugo server command with flags -D for including drafts in the build and --poll 700ms for checking file changes every 700 milliseconds, which enables live reloading. Mounts the current directory (your Hugo project) to /src inside the container, ensuring changes made locally are reflected inside the Docker container. Maps port 1313 on the host to port 1313 inside the container, making your site accessible at http://localhost:1313. Run Your Site: Launch your Hugo site with docker-compose up for development with live reloading. Stop with docker-compose down when done.\nWriting Posts in Obsidian Obsidian stores notes as Markdown files, and Hugo can publish Markdown directly. The clean workflow is to write each post as a Hugo page bundle inside the Obsidian vault, then sync those bundles into the Hugo repository before building.\nPost Folder Structure Create each post as its own folder:\nbvrA/posts/my-post/ index.md cover.jpg extra-image.jpg The index.md file contains the post body. Images sit next to the Markdown file, so they preview in Obsidian and publish correctly through Hugo.\nFront Matter Use YAML front matter so Obsidian Properties can edit the metadata:\n--- title: My Post author: - Bram Van Rompuy date: 2026-05-12 tags: - hugo - obsidian categories: - posts draft: true comment: true cover: image: cover.jpg --- Set draft: false when the post is ready to publish. Use normal Markdown links and images, such as ![](cover.jpg), instead of editor-specific links.\nSyncing to Hugo From the Hugo repository, sync the Obsidian post bundles into Hugo\u0026rsquo;s content/posts folder:\nbin/sync-obsidian-posts That command copies from ~/obsidian/bvrA/posts to content/posts, which is the folder Hugo builds from.\nPreviewing and Deploying After syncing content from Obsidian:\nRun the Hugo Server with Docker to view your site locally: If your website is already running, you can view it to see the update.\nOtherwise you can use the docker command: docker-compose up\nView Your Content: The synced posts will be visible on your local Hugo server, styled according to your site\u0026rsquo;s Hugo configuration.\nPreparing for Deployment Ensure your site is ready for deployment:\nAdd Changes and Commit: git add . git commit -m \u0026#34;Prepare site for deployment\u0026#34; Push to GitHub: git push -u origin main Adjust branch name as necessary. Conclusion: This part of the guide focuses on writing Markdown in Obsidian, syncing content into Hugo, previewing locally, and preparing for deployment.\nSetting Up Cloudflare Pages After pushing your Hugo site to GitHub, follow these steps to deploy your site with Cloudflare Pages:\nCloudflare setup with Github Create a Cloudflare Account and log in if you did not create one before.\nCreate a New Project on Cloudflare Pages by connecting to your GitHub repository. Cloudflare will ask for permission to access your GitHub repositories.\nSelect the Repository containing your Hugo site. Click on Add Account to add your account. You will be lead to the Github website to add your account.\nConfigure the Build Settings for your Hugo project. Set the framework preset and choose Hugo Set the build command to hugo and specify the build output directory, typically public.\nSet Environment Variables Add an environment variable HUGO_VERSION with the value of the Hugo version you are using, like 0.123.4.\nStart the Build Process. Cloudflare Pages will automatically build your site when you push changes to your linked GitHub repository.\nYou can see the process in the pages dashboard.\nDeployed website\nCloudflare Pages provides a seamless CI/CD pipeline, automatically building and deploying your site every time you push new changes to your GitHub repository.\nIt also assigns a unique .pages.dev domain to your project, though you can configure a custom domain if you prefer.\nConclusion Your website is now up and running using Obsidian for writing, Docker for clean local development, and Cloudflare Pages for sharing your site with the world.\nIn this guide, you learned how to use Obsidian and Markdown files to make writing and organizing your website straightforward. Docker helped keep things consistent, making sure your site works the same way everywhere. And by deploying with Cloudflare Pages, your site is now fast for visitors from all over, thanks to Cloudflare\u0026rsquo;s network.\nPutting your site online with Cloudflare Pages showed how everything just works together. Now, your site is out there for everyone to see, and any updates you make will show up online automatically.\nThis whole process shows a smart way to build and manage a static website today. Now that your site is live, you can keep adding to it, trying out new things in Hugo, or making it look even better.\nThanks for following this guide. I hope it helped you see how combining these tools can make managing a website a lot smoother.\n","permalink":"https://articles.bramvanrompuy.be/posts/setting-up-hugo-with-docker/","summary":"A practical Hugo workflow using Obsidian for writing, Docker for local development, and Cloudflare Pages for deployment.","title":"Building and deploying a Hugo website with Obsidian, Docker, and Cloudflare"},{"content":"Palmer Penguins Classification in Maya In this guide, we\u0026rsquo;ll see how to use the Palmer Penguins dataset in Maya to classify penguins.\nAbout the Palmer Penguins Dataset The Palmer Penguins dataset has data from three penguin types from Antarctica\u0026rsquo;s Palmer islands. These are Adélie, Gentoo, and Chinstrap. It has info like bill lengths and sizes, flipper lengths, and body weight. Check out the \u0026ldquo;Explore and Experiment\u0026rdquo; section for more.\nObjective Our goal is to train a machine learning model with this dataset. This model will guess a penguin\u0026rsquo;s type from its sizes.\nUsing Maya for Visualization I added a Maya file with a 3D penguin to make this guide more hands-on. You can change this 3D penguin\u0026rsquo;s sizes, like its bill length or flipper size. When you do, the trained model will guess its type. This shows how you can use the dataset in 3D.\nThis guide shows how to mix data science and 3D, showing how to use machine learning in tools like Maya.\nHow We\u0026rsquo;ll Proceed Code Walkthrough We\u0026rsquo;ll start with the Python code. I\u0026rsquo;ll show you the whole script and then explain each part. This will help you see how the machine learning model works, how it learns, and how it guesses.\nDiving into Maya After the code, we\u0026rsquo;ll look at Maya. We\u0026rsquo;ll check out the 3D penguin and see what you can change. This part helps you see how the 3D penguin\u0026rsquo;s sizes match the dataset\u0026rsquo;s data.\nBringing Code to Scene Last, we\u0026rsquo;ll use the Python script in Maya. When you change the 3D penguin\u0026rsquo;s sizes, you\u0026rsquo;ll see the model\u0026rsquo;s guess. This shows how the trained model works in 3D.\nBy the end, you\u0026rsquo;ll understand the data science of guessing penguin types and how to show it in 3D in Maya.\nDownload the Demo Files You can get all the files for this guide from the link below:\nhttps://github.com/BramVR/penguinFiles/tree/main\nDownload these files before you move on.\nSetting up the Environment To set up the environment, you need to install the required libraries. Create a file named \u0026lsquo;requirements.txt\u0026rsquo; and add the following content to it: If you are using Maya 2022:\nwerkzeug==2.2.3 tensorflow==2.10.0 scikit-learn==1.0 keras==2.10.0 fonttools==4.38.0 matplotlib==3.5.3 pandas==1.3.5 For Maya 2023+ (with Python 3.9 support):\nwerkzeug tensorflow scikit-learn keras fonttools matplotlib pandas protobuf Note: Maya 2023 introduced support for Python 3.9. This means that when working with Maya 2023 or later versions, you can use libraries compatible with Python 3.9. In contrast, Maya 2022 uses Python 3.7, which requires specific versions of libraries to ensure compatibility. As a result, with Maya 2023 and onwards, it\u0026rsquo;s generally safe to install the latest versions of the required libraries, as they are more likely to support Python 3.9.\nOpen up a new cmd window and navigate to your local Maya 2022 installation folder:\nC:\\Program Files\\Autodesk\\Maya2022\\bin For Maya 2023+:\nC:\\Program Files\\Autodesk\\Maya2023\\bin Now, run the following command to install the required libraries:\nmayapy -m pip install -r path_to_req/requirements.txt Code Walkthrough Below is the Python code that you will use for the demo. This code should be executed within Maya.\nCreate the Machine Learning model and saving it # Import necessary libraries and modules import pandas as pd import numpy as np import tensorflow as tf from tensorflow.keras.layers import Dense from tensorflow.keras.utils import to_categorical from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn.metrics import confusion_matrix import matplotlib.pyplot as plt # Load the dataset from the specified path data = pd.read_csv(r\u0026#39;path_to_csv/penguins-clean-all.csv\u0026#39;) # Map species names to numeric values data[\u0026#39;species\u0026#39;] = data[\u0026#39;species\u0026#39;].map({\u0026#39;Adelie\u0026#39;: 0, \u0026#39;Gentoo\u0026#39;: 1, \u0026#39;Chinstrap\u0026#39;: 2}) # Split the dataset into training (70%) and testing (30%) sets train_data, test_data = train_test_split(data, test_size=0.3, random_state=42) # Define the features for the model and standardize them scaler = StandardScaler() features = [\u0026#39;bill_length_mm\u0026#39;, \u0026#39;bill_depth_mm\u0026#39;, \u0026#39;flipper_length_mm\u0026#39;, \u0026#39;body_mass_g\u0026#39;] xtrain = scaler.fit_transform(train_data[features]) xtest = scaler.transform(test_data[features]) # Convert the species labels to one-hot encoded format ytrain_encoded = to_categorical(train_data[\u0026#39;species\u0026#39;]) ytest_encoded = to_categorical(test_data[\u0026#39;species\u0026#39;]) # Define the neural network model structure model = tf.keras.Sequential([ Dense(10, input_shape=(4,), activation=\u0026#34;relu\u0026#34;), Dense(3, activation=\u0026#34;softmax\u0026#34;) ]) # Compile the model specifying the optimizer, loss function, and evaluation metric model.compile(loss=\u0026#39;categorical_crossentropy\u0026#39;, optimizer=tf.keras.optimizers.SGD(learning_rate=0.01), metrics=[\u0026#39;accuracy\u0026#39;]) # Train the model using training data, and validate it using testing data history = model.fit(xtrain, ytrain_encoded, epochs=50, batch_size=16, validation_data=(xtest, ytest_encoded)) # Plot the model\u0026#39;s performance metrics (Accuracy, Loss, Confusion Matrix) fig, ax = plt.subplots(1, 3, figsize=(18, 6)) # Plot accuracy history for training and validation sets ax[0].plot(history.history[\u0026#39;accuracy\u0026#39;], label=\u0026#39;Train\u0026#39;) ax[0].plot(history.history[\u0026#39;val_accuracy\u0026#39;], label=\u0026#39;Test\u0026#39;) ax[0].set_title(\u0026#39;Model Accuracy\u0026#39;) ax[0].set_ylabel(\u0026#39;Accuracy\u0026#39;) ax[0].set_xlabel(\u0026#39;Epoch\u0026#39;) ax[0].legend() ax[0].invert_yaxis() # Plot loss history for training and validation sets ax[1].plot(history.history[\u0026#39;loss\u0026#39;], label=\u0026#39;Train\u0026#39;) ax[1].plot(history.history[\u0026#39;val_loss\u0026#39;], label=\u0026#39;Test\u0026#39;) ax[1].set_title(\u0026#39;Model Loss\u0026#39;) ax[1].set_ylabel(\u0026#39;Loss\u0026#39;) ax[1].set_xlabel(\u0026#39;Epoch\u0026#39;) ax[1].legend() # Generate and plot the confusion matrix conf_mat = confusion_matrix(test_data[\u0026#39;species\u0026#39;], np.argmax(model.predict(xtest), axis=-1)) cax = ax[2].matshow(conf_mat, cmap=\u0026#39;Blues\u0026#39;) fig.colorbar(cax, ax=ax[2]) ax[2].set_title(\u0026#39;Confusion Matrix\u0026#39;) ax[2].set_ylabel(\u0026#39;Actual\u0026#39;) ax[2].set_xlabel(\u0026#39;Predicted\u0026#39;) ax[2].set_xticks([0, 1, 2]) ax[2].set_yticks([0, 1, 2]) ax[2].set_xticklabels([\u0026#39;Adelie\u0026#39;, \u0026#39;Gentoo\u0026#39;, \u0026#39;Chinstrap\u0026#39;]) ax[2].set_yticklabels([\u0026#39;Adelie\u0026#39;, \u0026#39;Gentoo\u0026#39;, \u0026#39;Chinstrap\u0026#39;]) for i in range(conf_mat.shape[0]): for j in range(conf_mat.shape[1]): ax[2].text(j, i, conf_mat[i, j], ha=\u0026#39;center\u0026#39;, va=\u0026#39;center\u0026#39;) plt.tight_layout() plt.show() # Save the scaler scaler_filename = r\u0026#39;path_to_save_scaler\\scaler.save\u0026#39; joblib.dump(scaler, scaler_filename) # Save the trained model model_path = r\u0026#39;path_to_save_model\\model.h5\u0026#39; model.save(model_path) Code breakdown: You\u0026rsquo;re using the Palmer Penguins dataset. It has information on three types of penguins from three islands in Antarctica. The dataset includes details like flipper length and bill depth.\nStart by loading the dataset so you can work with it.\nChange the penguin names (like Adelie or Gentoo) from words to numbers. This makes it easier for the computer to understand.\nDivide the dataset. Use 70% of it to teach the model and 30% to test how well it has learned.\nAdjust the data so that all the measurements are on a similar scale. The StandardScaler helps with this.\nChange the penguin species names into a format that the model can use for classification. This is called one-hot encoding.\nBuild the model in steps:\nStart with an input layer for the data. Add a hidden layer that does most of the calculations. Finish with an output layer that gives the penguin species. Before the model starts learning:\nSet it up (compile it). Choose how it should measure how well it\u0026rsquo;s doing (categorical_crossentropy loss). Pick a method for it to improve (SGD optimizer). Let the model learn from the training data. Check how well it\u0026rsquo;s doing with the test data.\nIt\u0026rsquo;s good to see how the model is doing visually:\nLook at a graph of its accuracy. See how much it\u0026rsquo;s getting wrong (loss). Use a confusion matrix to see where it\u0026rsquo;s making mistakes. When you\u0026rsquo;re done, save your model for later use. Remember to:\nSave the model itself. Save the scaler too, so you treat new data the same way as the training data. Using the Saved Model in Maya # Import necessary libraries import numpy as np import tensorflow as tf from maya import cmds from sklearn.preprocessing import StandardScaler import joblib import pandas as pd # Load the saved scaler scaler_filename = r\u0026#39;path_to_save_scaler/scaler.save\u0026#39; scaler = joblib.load(scaler_filename) # Load the saved model model_path = r\u0026#39;path_to_save_model/model.h5\u0026#39; model = tf.keras.models.load_model(model_path) # Fetch custom attributes from Maya input_data = np.array([cmds.getAttr(\u0026#34;main_ctrl.billLength\u0026#34;), cmds.getAttr(\u0026#34;main_ctrl.billDepth\u0026#34;), cmds.getAttr(\u0026#34;main_ctrl.flipperLength\u0026#34;), cmds.getAttr(\u0026#34;main_ctrl.Mass\u0026#34;)]) # Convert the ndarray to a DataFrame with feature names features = [\u0026#39;bill_length_mm\u0026#39;, \u0026#39;bill_depth_mm\u0026#39;, \u0026#39;flipper_length_mm\u0026#39;, \u0026#39;body_mass_g\u0026#39;] input_df = pd.DataFrame([input_data], columns=features) # Use the loaded scaler to transform the input data input_data_scaled = scaler.transform(input_df) # Predict the species using the loaded model predicted_class = np.argmax(model.predict(input_data_scaled)) predicted_species =[\u0026#39;Adelie\u0026#39;, \u0026#39;Gentoo\u0026#39;, \u0026#39;Chinstrap\u0026#39;][predicted_class] cmds.setAttr(\u0026#34;annotationShape1.text\u0026#34;, predicted_species, type= \u0026#34;string\u0026#34;) Code Breakdown Start by importing all the necessary libraries. You\u0026rsquo;ll need numpy for numerical operations, TensorFlow for the neural network, Maya\u0026rsquo;s cmds for interaction with the Maya scene, and StandardScaler for data scaling. The joblib library is for loading the saved scaler, and pandas is used to handle data.\nLoad the previously saved scaler from its path.\nNext, load the trained model so that predictions can be made without needing to retrain.\nFetch the custom attributes from Maya. These are your input features: bill length, bill depth, flipper length, and mass.\nConvert the gathered attributes to a DataFrame, which is a table-like structure in pandas. This format is necessary for the next step, where we standardize the data.\nUse the loaded scaler to transform (standardize) your input data, ensuring it\u0026rsquo;s in the same format as the data used to train the model.\nPredict the species using the loaded model. The model provides a probability for each species, and argmax gives the one with the highest probability.\nDisplay the predicted species in Maya by setting the value of a text attribute, in this case, annotationShape1.text.\nDiving into Maya Maya Interface Upon opening the Maya scene, you\u0026rsquo;ll see a 3D penguin model. This model has adjustable properties linked to our dataset.\nAttributes Overview You can change these attributes in the rig:\nFlipper Length It\u0026rsquo;s about the penguin\u0026rsquo;s flipper size. It matches the flipper_length_mm in the dataset. Bill Length This is the penguin\u0026rsquo;s beak length. It matches the bill_length_mm in the dataset. Bill Depth This tells how thick the beak is. It\u0026rsquo;s the same as the bill_depth_mm in the dataset. Weight Range This goes from -1 to 1. -1 is for a thin penguin. 0 is the normal look. +1 is for a thicker penguin. Size This changes the penguin\u0026rsquo;s size. It goes from 50 to 98, showing different penguin sizes. Mass (Calculated Attribute) This is the penguin\u0026rsquo;s weight. It\u0026rsquo;s based on the weight and size. When you change those, this changes too. How Attributes Relate to the Dataset When you change things in Maya, you\u0026rsquo;re seeing our dataset in action. Changing these values lets you see different penguin \u0026ldquo;samples\u0026rdquo;. This hands-on method makes the data easy to understand in 3D.\nBringing Code to Scene Prepare Your Scene Open your Maya scene with the penguin and its changes. We\u0026rsquo;ll work with this 3D model. Loading the Dataset Before we start, make sure you can get to the Palmer Penguins dataset. Check the code to see where it\u0026rsquo;s looking for this data. This data teaches our model.\nTrain the Model within Maya In Maya, use the Python code from the section: \u0026ldquo;Create the Machine Learning model and saving it\u0026rdquo;. This code gets the data, gets it ready, sets up the model, and trains it. After it\u0026rsquo;s done training, it saves the model.\nAdjust Penguin Attributes using Maya After saving the trained model, go to the Maya rig settings. Change the Flipper Length, Bill Length, Bill Depth, Weight Range, Size, and Mass.\nMaking Predictions using Maya Use the Python code from the section: \u0026ldquo;Using the Saved Model in Maya\u0026rdquo;. When you change the penguin settings in Maya, run the code. It uses the saved model to guess the penguin type.\nExplore and Experiment Change the penguin rig settings and see what the model guesses. This helps you understand how the model thinks.\nYou can see below a few charts that show how the penguin data is spread out. This helps understand the different penguin types.\nConclusion You\u0026rsquo;ve now mixed data science and 3D modeling in Maya. You\u0026rsquo;ve seen how data, like penguin sizes, can be shown in 3D.\nBy now, you should get both the science of classifying penguins and how to show it in 3D in Maya.\n","permalink":"https://articles.bramvanrompuy.be/posts/neural_net_in_maya/","summary":"A practical walkthrough for training a Palmer Penguins neural net and using it in Autodesk Maya to classify penguins from scene controls.","title":"Run a neural net in Maya 2022+"}]