Controlled Skill Routing
Codex skill-maintenance architecture for reusable engineering lessons
Family updater routing, project-noise rejection and controlled agent memory
AI coding workflow article for durable software engineering automation
Codex skill routing and agent memory architecture
Overview
Reusable lesson routing summary
We built a small skill-maintenance system for Codex so reusable experience from real work can be promoted into global skills without turning those skills into project notebooks.
The central piece is $update-all. It is not meant to store domain knowledge itself. Its job is to analyze a completed conversation or project session, detect reusable lessons, and route those lessons to the correct family updater skill. Each family updater then decides whether anything should change.
This matters because active project work often produces a mix of different knowledge types:
- a real reusable failure mode
- a stack-specific workflow improvement
- a project-only implementation detail
- a private business rule
- a one-off workaround
- an existing lesson that is already covered well enough
Without a routing layer, all of those can easily end up mixed together. The goal of$update-all is to prevent that. It should help Codex learn from repeated work while keeping global skills clean, reusable, and safe.
The Core Rule
Default to no changes
The most important rule is: default to no changes.
$update-all should not update skills just because work happened. It should update skills only when the session produced a lesson that is likely to help future unrelated projects.
It should reject:
- project-specific implementation details
- company-specific business logic
- private URLs, credentials, tokens, domains, or payloads
- isolated hacks that do not generalize
- cosmetic wording preferences
- duplicate lessons already covered by existing skills
When nothing survives that filter, $update-all should report that no updater was routed and no files were changed.
Family Updater Skills
Durable ownership model
Instead of creating one giant global memory skill, we created separate family updater skills. Each one owns a broad technical area and follows the same pattern as $n8n-skill-update.
The current registered updater families include:
$n8n-skill-update$amocrm-update$frontend-skill-update$react-next-skill-update$astro-static-site-skill-update$django-skill-update$database-skill-update$deployment-skill-update$docker-nginx-skill-update$webhook-integration-skill-update$security-auth-skill-update$browser-qa-skill-update$document-pdf-skill-update$ios-builder
These are the only kinds of skills that belong in the $update-all registry: broad family updater or maintainer skills.
Focused subskills do not belong there. For example, skills such as browser-qa-localhost, deployment-vps-systemd, static-site-seo, or vite-react-build are useful focused skills, but they should be referenced by their family updater, not by $update-all directly.
How Routing Works
Candidate lesson classification flow
$update-all first analyzes the completed work and extracts candidate lessons. Then it routes each surviving lesson to the most specific family updater.
Some examples:
- MongoDB, MySQL, SQL migrations, query integrity ->
$database-skill-update - React, Next.js, Vite, TypeScript build behavior ->
$react-next-skill-update - general UI layout, responsive behavior, visual polish ->
$frontend-skill-update - browser screenshots, Playwright checks, console logs ->
$browser-qa-skill-update - VPS rollout, systemd, Nginx, production smoke checks ->
$deployment-skill-update - Docker Compose, Nginx proxying, TLS/container topology ->
$docker-nginx-skill-update - webhooks, provider callbacks, payload normalization ->
$webhook-integration-skill-update - tokens, auth, signatures, secrets, CORS/CSRF ->
$security-auth-skill-update
Specific ownership beats broad ownership. For example, a React/Vite build lesson should go to$react-next-skill-update, not to the more general $frontend-skill-update. A Docker/Nginx proxy lesson should go to $docker-nginx-skill-update, not to the broader deployment updater.
Multiple updaters may be used only when the lessons are genuinely independent. One lesson should not be duplicated across several families.
What Happens Inside a Family Updater
Updater decision model
Each family updater follows the same basic decision model:
- no update needed
- update one existing skill
- create one new focused subskill
- update itself if its family map or routing rules changed
The updater should prefer updating an existing skill. It creates a new focused subskill only when the lesson is narrow, recurring, reusable, and not already covered.
For example, if a session produced a real reusable MongoDB lesson, $update-all should route it to $database-skill-update. Then the database updater can decide whether to do nothing, update an existing database skill, or create a focused skill such as database-mongodb.
$update-all itself should not create database-mongodb. It only routes the lesson to the right owner.
Unowned Stack Triage
Handling new reusable domains
We added explicit logic for new stacks or tools that are not named in the registry.
The key rule is: a new stack is not automatically a new family.
First, $update-all checks whether the lesson belongs to an existing broad family. For example:
- MongoDB belongs under database
- S3 or object storage often belongs under deployment
- image/media rendering often belongs under frontend
- Stripe callbacks often belong under webhook integration, with security only if there is an independent signature/auth lesson
If the stack was only a small implementation detail and produced no reusable lesson, no update should happen.
If the stack produced a reusable lesson but no existing family clearly owns it,$update-all should not create a new updater silently. It should report the unowned lesson and recommend a candidate updater name.
Skill Creator Integration
Family updater registration rules
We also updated $skill-creator so future family updater skills are registered correctly.
When Codex creates or renames a skill whose primary job is maintaining a named family over time,$skill-creator must check $update-all and add one compact registry entry.
It should register only skills that are:
- family updater or maintainer skills
- durable across future projects
- distinct from existing updater families
- capable of choosing between no update, updating one existing skill, creating one focused subskill, or refreshing their own family map
It must not register:
- normal implementation skills
- one-off project skills
- focused subskills inside a family
- skills that merely mention update, upgrade, migration, or generation but do not maintain a family
This means if a new durable area appears later, such as Acumatica ERP, a future $acumatica-skill-update can be added cleanly to $update-all. But a normal Acumatica implementation helper would not be added to the registry.
Audit Results
Global skill registry verification
After active work, we audited the global skills directory to check whether $update-all was behaving correctly.
The important result: $update-all did not register focused subskills or project skills. Its registry still contained only broad updater/maintainer skills.
Several new focused skills were reviewed:
browser-qa-console-logsbrowser-qa-localhostdeployment-vps-systemdstatic-site-seovite-react-buildios-list-thread-details
Most of them were cleanly generalized. They described reusable workflows rather than private project details.
The main issue found was in the iOS family. $ios-builder contained the phrase For this workspace specifically, which made a global updater look tied to one workspace. We corrected that wording and generalized the surrounding section.
We also softened project-shaped wording in ios-list-thread-details, replacing narrow phrases such as CRM-like dialogs, React + Tauri + WebView, and dialog switch with broader terms such as contact-heavy apps, hybrid WebView apps, thread switching, and reusable mobile pane patterns.
Minor example cleanup was also done:
static-site-seonow uses more neutral static-export examplesvite-react-buildnow uses/old-subpath/and/legacy-prefix/instead of a project-shaped/design/example
Current Status
Controlled learning loop state
The architecture is now in a good state.
$update-all works as a manual command. It should not run automatically at the end of every session. The user will call it explicitly when they want Codex to analyze completed work and decide whether any global skill updates are justified.
The current model is:
- user manually calls
$update-all $update-allanalyzes the session- weak or project-only lessons are rejected
- reusable lessons are routed to the right family updater
- family updaters decide whether to change anything
- focused subskills stay inside their family, not in the global registry
- new family updaters require explicit user confirmation
This gives Codex a controlled learning loop: useful patterns can be preserved, but project details do not leak into global skills by default.
analyze completed session
extract candidate lessons
reject project-only and duplicate noise
route surviving lesson to specific family updater
let updater decide: none / update / create subskill / refresh map
report result without silent family creation