Why is VLAN Segmentation Overhyped in Small Networks?

Every new system or network engineer in the industry often starts by segmenting the existing network into slices, all in the name of “securing” it. In my 20 years of field experience, I’ve seen dozens of colleagues define 15 different VLANs even in 50-user offices, only to be crushed under the weight of their own rules. The answer to the question, Why is VLAN Segmentation Overhyped in Small Networks?, lies in that gray area where theoretical security textbooks clash with practical operational realities.

I’ve made this mistake repeatedly and had to correct it, both in the infrastructure of my own side products and in the medium-sized manufacturing facilities I’ve consulted for. In this article, I will explain, with concrete metrics and configuration examples, why VLAN segmentation in small networks is often an operational hindrance rather than a savior.

A Look at VLAN Segmentation in Small Networks: Theory vs. Practice

In theory, everything looks great: the Accounting department (VLAN 10) shouldn’t see Human Resources (VLAN 20), guests (VLAN 30) should only access the internet, and printers (VLAN 40) should be isolated. In classroom training or manufacturer sales brochures, this architecture is presented as “best practice.” However, the real world doesn’t work like that; when Ahmet from Accounting wants to access a shared folder on Elif’s computer in HR, or when he can’t print, your phone is the first to ring.

In a small network (e.g., environments with fewer than 100 hosts), each VLAN means a new IP block, a new gateway, a new DHCP pool, and most importantly, a new set of routing/firewall rules. When you divide a 100-device structure into 5 VLANs, you are essentially creating 5 different small networks that you need to manage.

Traditional Approach (Over-Segmentation):
[Users]        --(VLAN 10)--
[Printers]     --(VLAN 20)---+--> [Firewall / L3 Switch] --> [Internet]
[Servers]      --(VLAN 30)--/

Pragmatic Approach (Flat Network + Host-based Security):
[All Devices (Single Subnet /24)] ------> [Firewall] ------> [Internet]
  |_ (Software rules and endpoint security active)

In a past client project, I took over the network of an advertising agency with only 40 employees. There were exactly 8 VLANs defined in the office, and each department lived on its own island. When designers wanted to access the NAS device, packets would leave the edge switch, go to the core switch, then to the firewall, pass through rules, and return via the same physical path. Due to this unnecessary traffic, the 1 Gbps local network speed dropped to 150 Mbps, bottlenecked by the firewall’s IPS/IDS packet processing capacity (throughput).

Administrative Burden and the “Self-Hacking” Paradox

When you segment your network too much, your security level doesn’t increase; on the contrary, the likelihood of making mistakes multiplies due to management complexity. I call this “self-hacking” or operational blindness. After a while, so many rules accumulate that you start granting temporary Any to Any permissions on the firewall to solve urgent issues, and those temporary rules become permanent.

Take a look at the following Cisco IOS configuration. This ACL (Access Control List) mess, written just to connect two departments and a printer, turns into a nightmare as the network grows:

! Complex ACL example written on a Cisco Switch
ip access-list extended DEPT_ACCOUNTING_ONLY
 permit tcp 192.168.10.0 0.0.0.255 192.168.40.10 0.0.0.0 eq 9100
 permit ip 192.168.10.0 0.0.0.255 192.168.10.0 0.0.0.255
 deny ip 192.168.10.0 0.0.0.255 192.168.20.0 0.0.0.255
 deny ip 192.168.10.0 0.0.0.255 192.168.30.0 0.0.0.255
 permit ip any any
! You have to update these rules every time a new device is added.

Managing such rules becomes so difficult over time that the network administrator gives up when troubleshooting. As I also mentioned in my previous article on [related: firewall and gateway management], every manually managed and non-automated rule set eventually creates a security vulnerability. When you implement VLAN segmentation in small networks, you’re forced to reconfigure switch ports as trunk/access every time a new printer is acquired or a user’s desk changes.

⚠️ Operational Risk

If your network does not have dynamic VLAN assignment mechanisms (like 802.1X, RADIUS, etc.), port-based VLAN management makes you a slave to physical switches. Plugging a cable into the wrong port can halt an entire workflow.

IP Planning and Routing Deadlock: L3 Switch or Firewall?

One of the most critical architectural decisions when implementing VLAN segmentation in small networks is where to perform inter-VLAN routing. You have two options: either terminate and route this traffic on a Layer 3 switch, or push everything to the edge firewall device (Router-on-a-Stick).

If you push traffic to the firewall, the firewall’s port capacity and packet processing limits will create a bottleneck. For example, you might see the firewall CPU hit 100% when copying files to a backup server on the local network. If you perform routing on an L3 switch, you largely lose your ability to filter traffic between VLANs (apply security rules); L3 switches cannot perform stateful packet inspection like firewalls, they only use simple ACLs.

Method Performance (Throughput) Security Depth Ease of Management
L3 Switch Routing Very High (Wire-speed) Low (Stateless ACL only) Medium
Firewall Routing Low / Medium (CPU limited) Very High (Stateful Inspection / IPS) Difficult
Flat Network (Single Subnet) Highest Endpoint-Based Security Very Easy

Let me recount an incident I experienced while working on a production ERP system. Operator screens and barcode printers in the factory were on a separate VLAN, while the ERP server was on another. When operators pressed a button on the screen, the SQL query passed through the firewall to reach the server. During peak production hours (especially during shipment times), the firewall’s session table filled up, and packet loss began. To resolve the issue, we flattened the entire network architecture and moved the server and clients to the same L2 layer. Latency dropped from 12ms to 0.4ms.

DHCP, DNS, and Broadcast Traffic Myths: What Do Real Metrics Say?

One of the most common arguments made by VLAN proponents is to reduce the “broadcast domain” size. They claim, “There will be too much broadcast traffic on the network, switches will lock up, ARP packets will flood the network.” This argument was valid in the late 1990s when 10 Base-T hubs were in use. For today’s modern full-duplex gigabit switches and operating system network stacks, a broadcast domain of 254 devices is trivial.

Let’s analyze this with real data. In a flat network with an average of 150 active devices (/24 or /23 subnet), the metrics I obtained from a 1-hour packet analysis with Wireshark were as follows:

# Measuring broadcast packet ratio with Wireshark / tshark
$ tshark -r capture.pcap -q -z io,phs

===================================================================
Protocol Hierarchy Statistics
Filter: frames

eth                                      frames:1425300 bytes:1240500320
  ip                                     frames:1412000 bytes:1238400210
    udp                                  frames:85000   bytes:12400500
      dns                                frames:12000   bytes:1440000
  arp                                    frames:13300   bytes:851200   <-- Only 0.93%!
===================================================================

As can be seen, the ratio of ARP and other broadcast packets within all traffic is even below 1%. In a 1 Gbps bandwidth network, a few kilobytes per second of broadcast traffic can have no negative impact on performance.

Furthermore, you are forced to configure a separate DHCP helper (ip helper-address) or DHCP server for each VLAN. Defining separate blocks for each subnet in a Linux-based DHCP server (isc-dhcp-server or dnsmasq), managing IP conflicts, and ensuring DNS integration is unnecessary work for small teams.

Alternative and Pragmatic Security Approaches for Small-Scale Networks

So, if we’re not going to use VLAN segmentation, how do we ensure security? The answer is simple: move security from the network layer (L2/L3) to the application and endpoint layers. In the modern world, we call this zero-trust or host-based segmentation.

Having all devices on the same IP block in a company does not mean they can access each other without limits. All modern operating systems have built-in and centrally manageable firewalls (Windows Defender Firewall, Linux iptables/nftables, macOS PF).

# On a Linux server (e.g., running PostgreSQL), allow only specific IPs
# Using host-based firewall instead of traditional VLAN:
sudo iptables -A INPUT -p tcp -s 192.168.1.50 --dport 5432 -j ACCEPT
sudo iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 5432 -j REJECT

With this method, you can provide access control purely at the server level, without touching any switch configuration. Additionally, in Active Directory environments, you can disable file sharing (SMB – port 445) between all Windows clients with a single click using Group Policy (GPO). This way, you prevent a potential ransomware spread not at the network level, but directly at the operating system level.

I previously delved into the architectural details and software isolation techniques of this topic in my article on [related: network segmentation and zero-trust]. Applying those principles to small networks yields far more effective results than struggling with hardware VLANs.

Decision Matrix: When Should We Really Use VLANs?

Of course, we are not completely removing VLANs from our lives. There are some scenarios where using VLANs is truly essential and logical. The important thing is not to make it an obsession, but to implement it based on need.

The following decision matrix will provide you with a practical guide on whether or not you should implement VLAN segmentation in a network:

Situation / Need Is VLAN Necessary? Pragmatic Solution
Guest Wifi Network Yes A separate VLAN is essential to completely isolate guest traffic and prevent it from infiltrating the company network.
IP Phones (VoIP) Yes / Preferably Using a Voice VLAN is logical to prioritize QoS (Quality of Service) for VoIP packets.
Server Room / DMZ Yes Services exposed to the outside (web server, etc.) need to be isolated from the local network.
Department-Based Division No Use Windows GPO, local firewall rules, and Active Directory authorization.
Printers and IoT Devices No (Mostly) Fix printer IPs and allow access only via a print server.

If your network has fewer than 50 users and you don’t have critical servers providing external services, the ideal network design is: A single main subnet (users, printers, local servers) + a completely isolated single Guest VLAN. This simple structure keeps your operational burden close to zero, allowing you to dedicate time to application layer security, which is where you truly need to focus.

My Clear Stance

VLAN segmentation in small networks is often nothing more than a desire for technical personnel to “keep themselves busy” and feel like they are “managing a large enterprise network.” In reality, this over-engineering effort returns to you as outages, slowness, and administrative complexity, rather than security.

My clear stance on this matter is: Every line of configuration you don’t need is a source of a bug you will have to fix in the future. Instead of dividing the network, tighten security measures at the endpoints, establish strong authentication mechanisms, and keep your network infrastructure as simple, observable, and flat as possible. Ultimately, what will allow you to sleep peacefully at night are systems that are simple enough for you to manage, not complex switch rules.

yard-timekeeper: Stop YARD Timestamp Churn in Checked-In Docs

yard-timekeeper logo

yard-timekeeper logo

🕰️ I just released yard-timekeeper v0.1.0, a small RubyGem for Ruby projects that check generated YARD HTML into git.

It solves a very specific kind of documentation noise: timestamp-only churn.

The Problem

If your project publishes generated YARD documentation from a checked-in docs/ directory, you have probably seen this:

  1. You run the docs task.
  2. YARD regenerates HTML.
  3. Git reports changed files.
  4. You inspect the diff.
  5. The only change is the footer timestamp.

That is not a documentation change. It is build noise.

For projects that keep generated docs under version control, this creates unnecessary diffs, noisier pull requests, and extra review work. It also makes it harder to notice when documentation actually changed.

What yard-timekeeper Does

yard-timekeeper runs after YARD generates HTML and checks tracked files under docs/**/*.html.

If a file’s only change is the generated footer timestamp, it restores that file from git.

If the page has real content changes, it leaves the file alone.

The goal is not to hide documentation changes. The goal is to remove timestamp-only churn while preserving the signal that matters.

Install

gem install yard-timekeeper

Or add it with Bundler:

bundle add yard-timekeeper

Configuration

The supported workflow is through rake yard, so the post-process hook can run after YARD finishes.

In your Rakefile or documentation task setup:

require "yard/timekeeper"

Yard::Timekeeper.install_rake_tasks!(:yard)

Then generate docs with:

bin/rake yard

No .yardopts plugin entry is required for this integration. If you were experimenting with --plugin timekeeper, remove it for this workflow and use the rake integration instead.

Behavior

yard-timekeeper is intentionally conservative:

  • It only post-processes docs/**/*.html.
  • It only restores files already tracked by git.
  • It only restores files whose diff is timestamp-only.
  • It preserves real generated documentation changes.
  • It can be disabled with YARD_TIMEKEEPER_DISABLE=true.

That means new documentation pages, deleted pages, and pages with real content edits remain visible to git.

Why This Exists

I maintain a lot of Ruby gems, and many of them publish YARD docs from checked-in HTML. I want documentation generation to be repeatable without filling commits with timestamp changes.

Small tooling like this is not glamorous, but it improves the daily maintenance loop. Cleaner diffs mean easier review. Easier review means fewer mistakes.

Links

yard-timekeeper v0.1.0 is available now.

🕰️ May your docs be fresh, and your diffs quiet.

Mutation Testing for Dummies

Tests are important when it comes to implementing source code. Developers need ways to ensure the security of their source code as it becomes more complex. Increased complexity means that tests may become less capable of catching bugs. Mutation testing allows developers to test the weaknesses in their test suite, which shows what edge cases need to be handled and what constraints must be added to prevent farther bugs.

Mutation testing is a process developers use by creating bugs within their source code and running tests that should fail (failing tests mean a bug was found). When tests run, if the bug has survived, the tests meant to catch it were not thorough enough to handle certain edge cases. Mutation testing is classified as a white-box testing method which is best described as: a system that examines the internal functionality of an application. This is widely used in languages like Java and XML(a tool that stores and transports data). It’s purpose is to find code that may not be nested properly, discover new errors, and to understand how errors spread within the application. The goal is to prevent these errors from happening again in the future.

Mutation testing tools are used mostly for continuous integration and continuous delivery. CI/CD is a modern practice of automated building testing and releasing applications. This operation entails committing code to a shared repository frequently. Typically CI/CD without Mutation Testing requires a waiting period. Usually wait times without Mutation Testing with CI/CD jobs are due to a weak test suite. Mutation Testing can make that process much more efficient due to a decreased wait time for CI/CD jobs to finish.

The first implementation testing (documented) was by Timothy Budd as part of his PHD study titled “Mutation Analysis” in 1980. It was originally proposed by Richard Lipton in 1971. Then in 2004, “Certess inc.” added to much of the practice as part of hardware verification. Today, Mutation Testing is based upon two hypothesis; One being the Competent Programmer Hypothesis: this is where programmers write programs that are close to being correct. The other being The Coupling Effect: This is where simple mistakes can snowball into much more apparent and/or pervasive ones.

Mutation testing types can vary. One type of Mutation testing is called Value Mutation Testing. This is when values that are usually assigned to constants are changed to test whether tests fail or whether the mutations survive. Failing tests is a good thing, as it provides insight that the tests already implemented are thorough at catching bugs. Mutations surviving however, implies the opposite.

const num = 35;

changed:

const num = 3000005

The second type of Mutation Testing is Decision based. Decision based testing is when arithmetic operators are changed. Sometimes the changes can be slight, other times the changes can be apparent.

if(dog = cat)

changed:

if(dog >= cat)

Similar to Decision Based Testing, Statement Mutation testing is when one statement or variable name is replaced with another during reassignment. In this example giraffe is the variable that is being changed.

if(dog < cat)
  let giraffe = 10;
else
  let giraffe = 20;

changed:

if(dog < cat)
  let bunny = 10;
else
  let bunny = 20;

High Order Mutation Testing is when we change more than one single operation or value which can make it more difficult for tests to catch. These changes are usually too subtle to notice at first glance, however when you take a closer look you can see where the bug is.

let function (int a, int b){
 if (dog = 1):
 return cat < dog
 return cat > dog
}

changed:

let function (int a, int b){
 if (dog = 1):
 return cat <= dog
 return cat > dog
}

Mutation Testing can be simply implemented by first creating the source code. Once the code is created, inject mutants. Use one of the types of Mutation Testing to create small bugs that the test suite should hopefully catch. Next, run the tests. If a test fails on the line where the bug was created, then that test is solid. Otherwise, the bug survived and the tests are not thorough enough to prevent that bug from infecting the rest of the source code. Calculate how many tests failed and from there, rebuild the tests in accordance to any edge cases or general constraints that should be added.

The great thing about Mutation Testing is that it allows for more secure test updates. When a bug is not caught, developers can know exactly what to fix and how to make it more secure. Finding bugs that our unit tests can not find provides more efficient code. On the downside, it is very time consuming, and it is not available for Black Box Testing(evaluating functionality of an application without knowing it’s source code). In addition to this, some mutations are too complex to debug, making it difficult to implement corrective code or test against mutations. In all, Mutation Testing is a great method to implement when looking to create more secure code and build an even more secure test suite.

(https://www.computer.org/csdl/journal/ts/2024/05/10472898/1VpY3do6cyA)
https://stryker-mutator.io/docs/
https://www.computer.org/csdl/journal/ts/2024/05/10472898/1VpY3do6cyA(“IEEE Transactions on Software Engineering”; May 2024)
https://www.geeksforgeeks.org/software-testing/software-engineering-white-box-testing/
https://www.redhat.com/en/topics/devops/what-is-ci-cd(“redhat.com“; June 10, 2025)
https://bell-sw.com/blog/a-comprehensive-guide-to-mutation-testing-in-java/
(https://www.geeksforgeeks.org/devops/what-is-ci-cd/)
https://www.diffblue.com/resources/what-is-mutation-testing/
https://en.wikipedia.org/wiki/Mutation_testing
(https://www.geeksforgeeks.org/software-testing/software-engineering-black-box-testing/)
https://www.sciencedirect.com/science/article/abs/pii/S0950584909000688
https://www.cs.cmu.edu/~ckaestne/pdf/fse20hom.pdf

Qwen 3.6 27B and 35B MTP vs Standard on 16GB GPU

I tested Speculative decoding (Multi-Token Prediction, MTP) performance in Qwen 3.6 27B and 35B on an RTX 4080 with 16 GB VRAM.

For a broader view of token speeds and VRAM trade-offs across more models on the same hardware, see 16 GB VRAM LLM benchmarks with llama.cpp.

What MTP (Multi-Token Prediction) Is

Multi-Token Prediction is a form of speculative decoding built directly into certain model checkpoints. Instead of predicting one token per forward pass, the model carries extra “MTP heads” that propose several future tokens in a single step — then verifies them in parallel. If the guesses are accepted, the effective throughput rises without changing the output quality.

The Qwen 3.6 family ships both standard GGUF files and MTP-enabled variants. In llama.cpp, MTP is activated through:

--spec-type draft-mtp --spec-draft-n-max 3

--spec-draft-n-max is the key tuning knob. It sets how many speculative tokens the MTP head proposes at each step. Higher values give a potential speed boost but cost extra VRAM for the draft buffers — a real constraint on 16 GB cards.

What and How I Tested

I tested how the two Qwen 3.6 models behave with MTP enabled versus standard decoding on a GPU with 16 GB VRAM (RTX 4080).

To fit model weights and KV cache into VRAM I used heavily quantised variants:

  • Qwen3.6-27B-UD-IQ3_XXS and Qwen3.6-27B-UD-IQ3_XXS-MTP
  • Qwen3.6-35B-A3B-UD-IQ3_S and Qwen3.6-35B-A3B-UD-IQ3_S-MTP

Two context budgets are tracked per run:

  • Avg Ctx — the context size at which llama.cpp occupies ~14.8 GB VRAM, leaving other apps (Xorg, GNOME Shell, Cursor) a comfortable ~500 MB buffer.
  • Max Ctx — the largest context llama.cpp could allocate given that the same desktop apps already hold ~500 MB VRAM.

A key reason for keeping the average context at a practical target is that Hermes Agent — which I use as the primary AI assistant connecting to llama.cpp on this machine — requires at least 64 K context by default and will reject models with a smaller window at startup. Models below that threshold cannot maintain enough working memory for multi-step tool-calling workflows. For llama.cpp this means passing --ctx-size 65536 or larger. Any MTP configuration that compresses the average usable context significantly below 64 K is therefore unsuitable for daily Hermes workloads, which is why the Avg Ctx numbers in the tables below are the most decision-relevant ones.

Both KV cache quantisation levels were tested: q8 (higher quality, more VRAM) and q5 (lower VRAM, longer context). Be aware that moving from q8 to q5 KV cache can cause a noticeable quality drop — in my testing the degradation was significant enough to make q5 unsuitable for my workloads. The speed and context numbers for q5 are included for completeness, but you should test response quality on your own tasks before committing to it.

Qwen 3.6 27B MTP vs Standard

KV Cache q8

MTP max 1 MTP max 2 MTP max 3 MTP max 4 Standard (IQ3_XXS)
Prompt Speed 148 t/s 151 t/s 148 t/s 147 t/s 200 t/s
Gen Speed 65 t/s 75 t/s 73 t/s 75 t/s 45 t/s
Avg Ctx 40 K 40 K 40 K 30 K 80 K
Max Ctx 60 K 60 K 60 K 50 K 100 K

With q8 KV cache, MTP at --spec-draft-n-max 2 delivers ~67 % faster generation (75 vs 45 t/s) at the cost of halving the average context window from 80 K to 40 K. Prompt ingestion speed drops from 200 to ~150 t/s because MTP requires device-to-host transfers during the prefill phase.

KV Cache q5

MTP max 1 MTP max 2 MTP max 3 MTP max 4 Standard (IQ3_XXS)
Prompt Speed 145 t/s 144 t/s 141 t/s 139 t/s 191 t/s
Gen Speed 57 t/s 62 t/s 67 t/s 66 t/s 41 t/s
Avg Ctx 70 K 60 K 60 K 50 K 130 K
Max Ctx 100 K 100 K 90 K 80 K 160 K

Switching to q5 KV cache recovers meaningful context: --spec-draft-n-max 1 gives 70 K average context at 57 t/s — a 39 % generation speedup over standard decoding while still keeping the context window at a useful size. At --spec-draft-n-max 3 the context drops to 60 K but generation reaches 67 t/s (+63 %).

Qwen 3.6 27B Takeaway

MTP is genuinely useful for the 27B dense model. The sweet spot on 16 GB VRAM is:

  • q8 KV + --spec-draft-n-max 2 — best raw speed (75 t/s), context down to 40–60 K
  • q5 KV + --spec-draft-n-max 1 — best speed-vs-context balance (57 t/s, 70 K avg context)

Qwen 3.6 35B MTP vs Standard

The 35B model is a Mixture-of-Experts (MoE) architecture (35B-A3B means 35B total parameters, ~3B active per token). MoE models usually benefit more from MTP because the sparse routing keeps the MTP head computationally cheap relative to a full forward pass.

KV Cache q8

MTP max 1 MTP max 2 MTP max 3 MTP max 4 Standard (IQ3_S)
Prompt Speed 277 t/s 277 t/s 265 t/s 275 t/s 368 t/s
Gen Speed 186 t/s 189 t/s 180 t/s 171 t/s 146 t/s
Avg Ctx 15 K 10 K — — 80 K
Max Ctx 80 K 70 K 60 K 50 K 150 K

The MoE architecture delivers impressive raw generation speed with MTP (+27 % at max 1, +29 % at max 2 vs standard 146 t/s). But the practical problem is the average context. With q8 KV cache, even --spec-draft-n-max 1 only gives 15 K average context — barely enough for modest tasks. Higher draft depths have no viable average context at all on a 16 GB card.

This is the central VRAM cost question for MTP on consumer hardware: the extra draft buffers eat into the remaining VRAM budget directly, and the 35B-A3B model with q8 KV cache leaves very little headroom.

KV Cache q5

MTP max 1 MTP max 2 MTP max 3 MTP max 4 Standard (IQ3_S)
Prompt Speed 264 t/s 266 t/s 270 t/s 264 t/s 343 t/s
Gen Speed 151 t/s 147 t/s 137 t/s 131 t/s 122 t/s
Avg Ctx 10 K — — — 120 K
Max Ctx 120 K 110 K 110 K 80 K 200 K

q5 KV cache only marginally improves the average context story. --spec-draft-n-max 1 gives 10 K average context at 151 t/s. Standard decoding at q5 gives 122 t/s with 120 K average context.

Qwen 3.6 35B Takeaway

On a 16 GB GPU the 35B MoE model with MTP faces a hard wall: the average usable context collapses to 10–15 K tokens, making it impractical for real workloads. Standard decoding at 122–146 t/s with 80–120 K context is significantly more useful.

If you have 24 GB+ VRAM, the 35B + MTP combination becomes much more attractive — the context window issue disappears and you keep the speed benefit.

Choosing the Right --spec-draft-n-max Value

The question of how many speculative tokens to propose per step (--spec-draft-n-max) does not have a single right answer — it depends on both model architecture and available VRAM:

  • For 27B dense on 16 GB: --spec-draft-n-max 2 with q8 KV is the fastest, --spec-draft-n-max 1 with q5 KV is the most context-friendly.
  • For 35B MoE on 16 GB: --spec-draft-n-max 1 is the only option that keeps any usable context, and even then only marginally.
  • Higher values (3, 4) increase VRAM pressure without proportional speed gains — at max 4 you’re spending roughly the same extra VRAM as max 2 but gen speed doesn’t keep pace.

How to Enable MTP in llama.cpp

Make sure you use an MTP-enabled GGUF (the filename contains MTP). If you are new to llama.cpp flags, the llama.cpp Quickstart with CLI and Server covers all the fundamentals. Then launch llama-server or llama-cli with:

llama-server 
  --model Qwen3.6-27B-UD-IQ3_XXS-MTP.gguf 
  --ctx-size 40000 
  -ngl 99 --flash-attn on 
  --cache-type-k q8_0 --cache-type-v q8_0 
  --spec-type draft-mtp 
  --spec-draft-n-max 2

For q5 KV cache, replace q8_0 with q5_1 or q5_0 and adjust --ctx-size upward:

llama-server 
  --model Qwen3.6-27B-UD-IQ3_XXS-MTP.gguf 
  --ctx-size 80000 
  -ngl 99 --flash-attn on 
  --cache-type-k q5_1 --cache-type-v q5_1 
  --spec-type draft-mtp 
  --spec-draft-n-max 1

MTP is activated automatically once llama.cpp sees the MTP heads in the GGUF file and --spec-type draft-mtp is set.
So standard Qwen3.6-27B-UD-IQ3_XXS.gguf will not work in MTP mode, you will need Qwen3.6-27B-UD-IQ3_XXS-MTP.gguf.
But the Qwen3.6-27B-UD-IQ3_XXS-MTP.gguf can work in both Speculative decoding mode and autoregressive one.

Conclusion

On a 16 GB GPU (RTX 4080), with these quants, llama.cpp’s MTP is a clear win for Qwen 3.6 27B and a net negative for Qwen 3.6 35B in practical use:

Qwen 3.6 27B (IQ3_XXS) — MTP is worthwhile:

  • q8 KV + MTP max 2 → ~67 % faster generation, context 40–60 K (vs 80–100 K without MTP)
  • q5 KV + MTP max 1 → ~39 % faster generation, context 70–100 K (vs 130–160 K without MTP)
  • Good balance of speed and VRAM efficiency at --spec-draft-n-max 2

Qwen 3.6 35B (IQ3_S) — MTP is not practical at 16 GB:

  • Generation speed is 27–29 % higher but average context collapses to 10–15 K at q8, 10 K at q5
  • Standard decoding at 122–146 t/s with 80–120 K context is more useful for real tasks
  • The situation improves substantially on 24 GB+ VRAM

On paper, q5 KV cache is the obvious answer for maximising context window while keeping MTP speed gains — but in practice the quality drop moving from q8 to q5 can be significant. Test q5 on your own tasks before adopting it; for my workloads the degradation was unacceptable, and q8 with a tighter context budget remains the better trade-off.

For the wider picture of LLM serving options and infrastructure trade-offs, see the LLM Hosting in 2026 pillar and LLM Performance in 2026. If you are tuning Qwen 3.6 sampler settings alongside MTP, the Agentic LLM Inference Parameters Reference for Qwen 3.6 and Gemma 4 is a useful companion.

PID Tuning That Actually Converges: Ziegler-Nichols and the Root Locus View

The PID controller is the workhorse of industrial control. It runs temperature loops, motor drives, flow valves, and flight surfaces. The algorithm itself is three terms and a line of arithmetic. The hard part has never been the controller — it is choosing the three gains. Set them too low and the system crawls toward its target. Set them too high and it oscillates, or worse, runs away. “Tuning” is the craft of finding the gains that land in between.

This article covers two complementary ways to tune a PID loop: the empirical Ziegler-Nichols method and the graphical root locus view — and shows how they describe the same boundary from different sides.

Why this calculation matters

An untuned loop is not a minor inconvenience. A sluggish temperature controller wastes energy and product. An oscillating position loop hammers the mechanics and shortens equipment life. An unstable loop is a safety incident. Between those failure modes sits a band of gains that gives a fast, well-damped response — and tuning is how you find that band on purpose rather than by trial and error at the plant.

The two methods matter because they suit different situations. Ziegler-Nichols needs only a simple experiment on the real system and no model. Root locus needs a model but, in return, shows you the whole stability picture at a glance. A capable engineer reaches for whichever the situation allows.

The two methods

Ziegler-Nichols, ultimate-sensitivity form. Disable the integral and derivative terms, leaving proportional control only. Raise the proportional gain until the loop sits in a steady, sustained oscillation — neither growing nor decaying. Record two numbers: the gain that produced it, the ultimate gain Ku, and the period of the oscillation, the ultimate period Pu. The classic PID settings then follow from a small table:

Kp = 0.6 * Ku
Ti = Pu / 2          (integral time)
Td = Pu / 8          (derivative time)

In the parallel form the integral and derivative gains are Ki = Kp/Ti and Kd = Kp·Td.

Root locus. This is the model-based companion. The root locus is a plot of where the closed-loop poles travel in the complex plane as the proportional gain increases from zero. Poles in the left half-plane mean a stable, decaying response; poles crossing into the right half-plane mean instability. The exact gain at which the locus crosses the imaginary axis is the ultimate gain Ku, and the frequency at that crossing gives the ultimate period through Pu = 2 pi / omega. The two methods are looking at the same stability boundary — Ziegler-Nichols finds it by experiment, root locus finds it by geometry.

A worked example

Suppose the ultimate-sensitivity experiment on a process gives an ultimate gain Ku = 8 and an ultimate period Pu = 2 seconds. Apply the Ziegler-Nichols PID table:

Kp = 0.6 x 8   = 4.8
Ti = 2 / 2     = 1.0 s   ->  Ki = Kp / Ti = 4.8
Td = 2 / 8     = 0.25 s  ->  Kd = Kp x Td = 1.2

So a starting controller is Kp = 4.8, Ki = 4.8, Kd = 1.2. Note the word starting. Classic Ziegler-Nichols deliberately targets a fairly aggressive response — roughly quarter-amplitude decay, meaning each overshoot is about a quarter of the one before. That is lively. For a loop that must not overshoot, back the proportional gain down by 20 to 40 % from the table value and re-check. Ziegler-Nichols gets you into the right neighbourhood fast; the final trim is yours.

The root locus view of the same system would show the closed-loop poles starting on the open-loop poles, sweeping outward as gain rises, and crossing the imaginary axis exactly at gain 8 — confirming Ku — at a frequency of pi rad/s, which gives Pu = 2 pi / pi = 2 s. Same two numbers, reached without ever oscillating the real plant.

Common mistakes

Running the Ku experiment on a fragile plant. Driving a real system into sustained oscillation is not always safe — think of a large thermal mass or a machine with travel limits. When oscillating the plant is risky, use a model and root locus, or a relay-feedback test that bounds the swing.

Leaving the gains at the textbook values. The Ziegler-Nichols table is a first guess tuned for disturbance rejection, not for a smooth setpoint response. Treat its output as iteration zero.

Over-using the derivative term. Derivative action amplifies measurement noise. On a noisy sensor a large Kd makes the actuator chatter. Filter the derivative, or reduce it, before blaming the loop.

Forgetting the sample rate on a digital controller. A discrete PID is not the continuous one. If the loop runs too slowly relative to the process dynamics, the phase lag from sampling erodes the stability margin the tuning assumed.

Try the interactive NovaSolver calculator

Tuning is much easier to learn when you can see the step response change as you move the gains. The PID tuning tool on NovaSolver lets you apply Ziegler-Nichols settings and then drag Kp, Ki, and Kd to watch overshoot, settling time, and steady-state error respond in real time.

Related calculators

  • PID controller response — to see how each of the three terms shapes the output on its own.
  • Root locus — the graphical stability picture, including the imaginary-axis crossing that defines Ku.
  • Digital PID discretization — how sample rate and discretization affect a controller running in software.

The full set is in the PID and controls tools hub.

Closing note

PID tuning has a reputation as a black art, but the two methods here remove most of the mystery. Ziegler-Nichols gives you a fast, model-free starting point from one experiment. Root locus gives you the whole stability map if you have a model, and it pinpoints the exact boundary the experiment is feeling for. Use them together: locate the stability edge, back off to a sensible margin, and trim against the response you actually want. That is tuning done deliberately rather than by luck.

Cx Dev Log — 2026-04-27

Nothing landed today in the Cx language project, making it a perfect opportunity to delve into something that’s been lurking in the background: the merge gap between submain and main. This isn’t just a gap; it’s the biggest structural challenge we’re facing right now.

The Merge Gap

Submain sits 21 commits ahead of main. And these aren’t trivial updates. These commits incorporate crucial updates: Phases 10 and 11 of the IR backend covering while/for/loop/break/continue lowering, unary expression transitions, a reworked error model with Result, integer overflow enforcement, optional semicolons throughout the language, comprehensive audits on parser/semantic/interpreter agreement as well as memory boundary soundness, a diagnostics readability pass, and the version bump for the roadmap to v5.0—with all 9 hard blockers resolved.

Main is still at version 4.8 with 8 hard blockers hanging overhead. Test data paints the same picture: main holds at 78/78 tests, whereas submain runs ahead with 117/117. Merging these would address a significant 39-test disparity in one swift action.

Every daily log over the past week has identified “merge submain into main” as a critical next step. Yet, the merge isn’t happening. It’s not a crisis at this moment, but the longer submain stays diverged, the more complex merging will become if new work gets added to either branch independently.

Branch Housekeeping

Alongside the merge gap, there’s another mess that needs addressing: the proliferation of daily-log branches. 26 branches from March 29, 2026, to April 26, 2026, sit unmerged in the remote repository. Only two—March 28 and March 30—made it via PRs. The remaining 24 persist as operational noise. They’re not blocking anything critically, but tidying up would certainly clean the remote branch list.

Yesterday’s Expectations vs. Reality

The last log suggested four next actions: the submain to main merge, progressing through Phase 11 expression lowering, addressing the IR backend test failure, and clearing out the daily-log branch backlog. None of this happened. Development was idle, with zero new contributions made—in stark contrast to the day’s predictions.

What’s Next

What’s striking is how the priority list has remained consistent, which says something about our current state. Based on leverage:

  1. Merge submain to main. This is the highest priority action. It upgrades main from v4.8 to v5.0, addresses all remaining hard blockers, and closes the 39-test gap.
  2. Continue Phase 11 expression lowering. Remaining unsupported expressions like ArrayLit, Index, MethodCall, StructLit, HandleVal, HandleDrop, and Range in lower.rs need handling.
  3. Fix the IR backend test failure. Submain has 123 tests passing against one failing. Fixing this would render the suite fully green.
  4. Clean up stale daily-log branches. While they don’t block progression, tidying these 24 branches would help reduce clutter.

Quiet days will inevitably dot any development journey. The project’s not in crisis mode by any means, but the delay in merging submain is flagging as an overdue task on the agenda. Whenever development picks up, this merge should be top of the to-do list.

Follow the Cx language project:

Originally published at https://cx-lang.com/blog/2026-04-27

Building PROTO RECON — From Vague Idea to a Browser-Based Tactical HUD

Introduction

This post walks through how PROTO RECON — an experimental app that combines phone sensors with in-browser ML — evolved from pre-coding requirements to its current implementation. I’ve verified the UI behavior, but I’m still at the stage of reading the source code for the first time. Here I organize the original requirements and an AI-generated source map as a foundation for deeper dives in future posts.

Note: This is still in testing. Performance tuning for heavy workloads hasn’t been done yet — use at your own risk.

Where It Started

It began with a vague question: Can we build a digital user experience on top of the real world? The seed of the idea was whether today’s phone sensors could overlay something like the robot-view or cockpit-view perspectives from old sci-fi and games onto live scenery.

Before writing code, I summarized the requirements, turned functional specs into prose through dialogue with AI, and then started development.

Technology Choices (Before Implementation)

Area Adopted / Considered Notes
Object detection TensorFlow.js (COCO-SSD / BlazeFace) “Object recognition” as the core of the robot-view feel, fully in the browser
Map Leaflet Familiar “you are here” display from games
Noise / effects Initially GLSL / three.js Wanted to reuse past effect-building experience

The noise layer was added later. When I had Cursor implement it, three.js was skipped in favor of Canvas 2D and CSS for CRT-style effects. Writing directly without extra modules kept things lighter — a decision that has paid off on real devices.

Gyro, compass, altitude (on supported devices), GPS, and similar sensors were also in scope, wired to the HUD and minimap.

Requirements at the Time (Summary)

The planning document — built through repeated conversations with AI — was titled Tactical Recon & Guidance Terminal “PROJECT: LOCK-ON (working title)”. At that point, sensor accuracy led us to assume Phase 2 would require a Unity (native) rewrite. After coding with Cursor, however, things moved better than expected even without three.js, and the outlook shifted to Phase 1 might be achievable on the web alone.

Core Concept

  • Visuals: Monochrome green military-terminal UI, scan lines, noise
  • AI: Real-time object detection for targeting and lock-on
  • Immersion: Gyro, altitude, BEEP sounds — instrument and audio feedback
  • Future: VR-goggle HUD, gaze control (concept stage)

Plan vs. Current State

Item Plan (as of Apr 2026) Current (PROTO RECON)
Stack Next.js + Three.js + TF.js Vite + vanilla JS + TF.js (no Three.js)
Altimeter Barometric sensor (native assumed) Geolocation altitude (supported devices only)
Map (lightly specified in the plan) Leaflet minimap implemented
Missile effect Tap to fire a virtual missile Not implemented
Phase 2 Unity Barometric altitude, haptics, etc. Not started (web prototype continues)
Distance display (mostly undefined in planning) WebXR hit-test measurements only (no bbox-estimated distance)

The full planning document is collapsed below due to length.

Full planning document (PROJECT: LOCK-ON draft)

Tactical Recon & Guidance Terminal App — “PROJECT: LOCK-ON (working title)” Planning Document

This document summarizes a concept for a smartphone camera-filter app that blends retro-futuristic UI with modern AI.

1. Project Overview

A “play” app that recreates the experience of a military recon terminal from 1980s–90s sci-fi films and games, using live smartphone camera feed and AI.

2. Core Concept

  • Visuals: Monochrome green evoking old terminals. Retro feel via scan lines and noise.
  • AI interaction: Automatic targeting and lock-on through real-time object detection.
  • Immersion: Gyro, barometric sensor (altimeter), BEEP feedback.
  • Extensibility: HUD and gaze control with VR goggles (e.g. Hacosco) — concept stage.

3. Main Features (excerpt)

3.1 Video filter

  • Green overlay (night-vision goggle texture)
  • Edge bar graphs and scan indicators

3.2 AI lock-on

  • Automatic moving-object detection with green bounding-box tracking
  • SE accelerates when the target stays in the reticle center
  • Lock complete: box turns red, warning sound
  • Tap to fire a virtual missile (effect)

3.3 Sensor-linked instruments

  • Gyro: pitch and roll numeric display
  • Altimeter: relative altitude from barometric sensor (iPhone, etc.)

4. Development Phases (roadmap at the time)

  • Phase 1: Web prototype (Next.js + Three.js + TF.js). Shareable via URL.
  • Phase 2: Unity port. Barometric altitude, vibration.
  • Phase 3: VR/XR. Stereoscopic display, gaze lock-on.

5. Distribution Strategy

Dev logs on Qiita / Zenn, short-form video on social, posts on Reddit, etc.

Created: April 29, 2026 (planning stage)

How Development Proceeded

Coding was delegated to Cursor, adding features one prompt at a time until the current state. For prompts, I load the requirements document into Gemini and ask what kind of prompt would work best. I’ve checked JS behavior through the UI, but reading the source comes next. The file list and dependency diagram below were AI-generated and cleaned up for this article.

Source Layout

HTML (root level)

File Role
index.html Main screen. BIOS/splash, boot progress, camera feed, reticle/detection overlay, compass/altitude tape, minimap, CRT effects, etc. Loads /src/main.js as a module
privacy.html Privacy policy (static page: collected data, purpose of use, etc.)
oss-notice.html Static OSS / third-party license notice

src/ — runtime files

Entry, config, UI

File Role
main.js App hub. Boot sequence, camera acquisition, start/stop of modules, HUD updates, user actions (start/stop, lock, .etc.)
appConfig.js Dev tuning defaults (camera, CRT, object detection, face mask, minimap, motion linkage, live health, etc.)
applyDevConfig.js Applies devConfig.crt as CSS variables on document.documentElement (vignette, scan lines, etc.)
uiLexicon.js Single source for UI copy (splash, status, errors, minimap labels, etc.) including functions to apply text to HTML
style.css Global layout, HUD, CRT, minimap, reticle styles (green military UI)

Video & AI inference

File Role
tfBackend.js One-time TensorFlow.js backend init (WebGPU → WebGL → CPU)
personDetection.js COCO-SSD object detection. Bounding-box drawing, lock zone, target events, inference health notifications
facePrivacy.js BlazeFace face detection and masking (privacy overlay)
lockTrackKeys.js Stable IDs for targets in the lock zone (position quantization + match with previous frame)

Distance & AR

File Role
objectRange.js Distance to target on HUD — measured distances only (WebXR, etc.); no bbox-estimated distance
webxrHitTestRange.js WebXR immersive-ar + hit-test for real-world distance (m) fed into objectRange

Navigation, map, sensors

File Role
compass.js Device heading (compass) wrapper. Heading parse, cardinal labels, iOS permission request
miniMap.js Geolocation watch and minimap facade (position, heading, speed, altitude callbacks)
miniMapLeaflet.js Leaflet map init, current-location marker (heading arrow), tile display
motionHud.js Tilt β/γ from DeviceOrientation for the right vertical tape HUD
motionStabilityLink.js Links tilt/shake to reticle/minimap parallax and notifications (including vibration)

Feedback & monitoring

File Role
liveHealth.js Lightweight live health monitoring (inference latency, consecutive failures, video stall) and HUD notifications
audioFx.js Web Audio API for boot, detection, lock-on, error sounds, etc.

src/ — tests only (not used on screen)

File Role
lockTrackKeys.test.js Unit tests for lockTrackKeys.js
liveHealth.test.js Unit tests for liveHealth.js
tfBackend.test.js Unit tests for tfBackend.js

Dependencies (overview)

flowchart TB
  index[index.html] --> main[main.js]
  main --> config[appConfig / applyDevConfig / uiLexicon]
  main --> cam[camera + HUD]
  main --> det[personDetection]
  main --> face[facePrivacy]
  det --> tf[tfBackend]
  face --> tf
  main --> map[miniMap]
  map --> leaf[miniMapLeaflet]
  main --> nav[compass / motionHud / motionStabilityLink]
  main --> range[objectRange]
  range --> xr[webxrHitTestRange]
  main --> health[liveHealth]
  main --> sfx[audioFx]

main.js ties almost everything together; appConfig.js settings and uiLexicon.js copy drive behavior and display across modules.

Closing — What to Read Next

Future posts will open the source and go deep feature by feature. The study roadmap below is the planned reading order (01 is done with this article’s file map).

Source reading roadmap (32 parts — click to expand)

Stage 1 — Config and skeleton (no permissions needed)

# Title / theme Main files Core tech Common pitfalls
01 Full map — roles in src/ - Which file owns what Memorizing filenames without reading code
02 devConfig — behavior remote control appConfig.js Camera, detection, CRT key meanings One change affects the whole app
03 Single source for UI copy — uiLexicon uiLexicon.js, index.html Centralized strings and error messages Finding leftover hardcoded text
04 CRT tokens to CSS — applyDevConfig applyDevConfig.js, style.css JS config → CSS variables (:root) Typo in variable name breaks styles
05 HTML skeleton — splash & HUD DOM index.html Layer stack (video / canvas / HUD) z-index vs hidden attribute

Stage 2 ⭐⭐ — Look and boot “theater” (testable without camera)

UI animation and audio control without device permissions.

# Title / theme Main files Core tech Common pitfalls
06 Splash, BIOS, scramble text main.js, style.css First-run UI flow, timer control Duplicate timers, garbled text
07 Boot overlay — progress and steps main.js Async status during startup Failed step never transitions to error
08 Web Audio — play after click audioFx.js AudioContext and user gesture Autoplay policy, silent bugs
09 Reticle SVG and fixed layout index.html, style.css Center-fixed CSS layout Why motionStabilityLink was disabled
10 Live HUD frame and CRT noise style.css, main.js Scan-line effects over video Rendering cost of noise canvas

Stage 3 ⭐⭐⭐ — Media, sensors, tapes (real device required)

# Title / theme Main files Core tech Common pitfalls
11 Camera — getUserMedia and rear preference main.js facingMode, stream acquisition iOS permission timing, connection hang
12 Video presentation — object-fit: cover style.css, main.js Hidden → visible preview transition Unintended camera zoom
13 Full boot sequence — startCamera main.js After tap: camera → model load Failure logs when changing parallel vs sequential
14 Heading tape — continuous angle and N jump compass.js, main.js 0°–360° unwrap, SVG control Clipping at north (N), insufficient padding
15 Tilt tape — β / γ and motionHud motionHud.js, main.js DeviceOrientation deviceorientationabsolute behavior differences
16 Altitude tape — GPS altitude callback miniMap.js, main.js Geolocation API Devices where altitude is null
17 Minimap — Leaflet and OSM/CARTO miniMap.js External tile map, marker Handling location denial
18 Speed HUD — km/h from coords.speed miniMap.js Speed from coordinate changes Speed shows -- when stationary

Stage 4 ⭐⭐⭐⭐ — Detection, privacy, ML

# Title / theme Main files Core tech Common pitfalls
19 TF.js backend — WebGPU→WebGL→CPU tfBackend.js Graphics API fallback Unsupported devices, first compile, heat
20 COCO-SSD intro — ensureModel and throttling personDetection.js Model load, inference throttling CDN load failure, dynamic import()
21 Detection box drawing — drawVideoCover and canvas personDetection.js Drawing aligned to video aspect Coordinate drift, DPR
22 Lock-on — zone, dwell, key stabilization personDetection.js Tracking ID, smoothing ID swaps, flicker
23 Distance — WebXR hit-test only objectRange.js, webxrHitTestRange.js Measured distance on HUD No distance on unsupported devices
24 Face mosaic — BlazeFace and canvas compositing facePrivacy.js rAF + throttled inference Draw cycle vs inference collision
25 Mosaic interval and heat — intervalMs facePrivacy.js Inference FPS vs load tradeoff Device freeze, thermal throttling

Stage 5 ⭐⭐⭐⭐⭐ — Integration, ops, errors

# Title / theme Main files Core tech Common pitfalls
26 Live health — latency, video stall liveHealth.js Main-thread stall monitoring False positives under load spikes
27 Startup error handling — timeout and catch main.js Async error gaps Model load timeout
28 Second visit and cache vite.config.js PWA, Vite build failed to fetch dynamically imported module
29 Revisit and restart — model disposal main.js Memory leak prevention Second run extremely heavy
30 motionStabilityLink — why parallax was cut motionStabilityLink.js Parallax vs readability Janky rendering
31 Integration — main.js orchestration main.js (full) Lifecycle management Tight coupling breaks on touch
32 Retrospective — bug list and what’s next App overall Settling on load mitigation Defining “fatal bugs are gone”

The next post will start with roadmap #02 (appConfig.js).

Compliance Drift

The compliance industry was originally created to stop institutions from becoming dangerous.

That was the idea, anyway.

Too much money corrupts judgment.
Too much access corrupts restraint.
Too much unchecked authority eventually produces behavior no institution would publicly admit to intentionally designing.

So modern corporations built compliance:
frameworks,
controls,
audits,
governance boards,
risk scoring,
mandatory reporting structures,
ethics certifications,
behavioral standards.

An operational immune system designed to prevent institutions from quietly mutating into predatory environments.

And for a while, parts of it worked.

Financial controls reduced fraud.
Access reviews limited privilege abuse.
Security governance prevented catastrophic failures.
Healthcare compliance protected patient data.
Aviation compliance kept aircraft from falling out of the sky due to executive optimism and spreadsheet-based engineering.

Real compliance matters because real systems drift.

That is one of the foundational truths of cybersecurity:
every sufficiently complex environment eventually diverges from its documented secure state.

Permissions accumulate.
Exceptions multiply.
Temporary workarounds harden into permanent architecture.
Trust assumptions fossilize into invisible vulnerabilities.

The industry even has a term for it:
compliance drift.

The slow divergence between documented reality and operational reality.

Which is interesting, because many modern institutions now suffer from the exact same condition psychologically.

The paperwork says:
ethical,
inclusive,
safe,
accountable,
human-centered.

The operational environment often behaves more like politically calibrated survival infrastructure.

Not through explicit hostility.
Through strategic ambiguity.

Never fully rejecting.
Never fully accepting.
Never clearly defining the problem.
Sustaining just enough uncertainty to keep people psychologically occupied while accountability remains beautifully diffused across process, policy, and “concern.”

That ambiguity becomes operationally useful.

A person who feels fully rejected eventually disengages.
A person who feels secure gains stability.

But a person suspended between possibility and threat often continues producing while trying to resolve the uncertainty itself.

That is where things become interesting.

Because eventually the compliance officer arrives.

Every institution has one.

Expensive suit.
Controlled tone.
Carefully moderated body language.
The emotional neutrality of someone trained to convert institutional panic into procedural language.

He enters the room carrying governance vocabulary like ceremonial equipment:
policy,
alignment,
conduct,
professionalism,
expectations,
culture.

The language always sounds civilized.

That is the first warning sign.

Modern institutions rarely pressure people directly anymore. Direct coercion creates discoverable evidence. Instead they construct environments where pressure emerges beneath layers of perfectly reasonable language.

That is the real innovation of contemporary compliance culture:
the ability to operationalize discomfort without appearing operationally aggressive.

The meeting is never technically hostile.

Which is exactly why it works.

Somewhere inside a conference room with artificial plants and over-air-conditioned air, a compliance representative translates one man’s discomfort with a woman’s facial expressions into an institutional compliance concern, quietly demonstrating how easily subjective perception mutates into organizational pressure once hierarchy becomes involved.

Not illegally enough to trigger escalation.

Just enough to establish gravitational force.

That is the modern institutional specialty:
noncompliant methods deployed in defense of compliance optics.

And everyone involved understands the contradiction immediately.

Nobody says it aloud.

Because the meeting is not actually about ethics.

It is about narrative containment.

That is what large portions of the compliance industry quietly became during the AI era:
not governance infrastructure,
but liability choreography.

Meanwhile outside the conference room, institutions are deploying autonomous AI systems faster than governance departments can meaningfully interpret operational risk. Executives demand aggressive AI integration while simultaneously hosting “Responsible AI” summits assembled from slide decks, buzzwords, and optimistic forecasting nobody fully believes privately.

The infrastructure underneath many companies now resembles a probabilistic fever dream:
autonomous agents,
third-party APIs,
contractor ecosystems,
identity sprawl,
shadow AI tooling,
compliance frameworks stapled desperately onto systems nobody completely understands anymore.

And somehow compliance departments are expected to make all of this appear governable.

So the theater intensifies.

More certifications.
More workshops.
More ethics language.
More behavioral modules narrated in the emotional cadence of institutional anesthesia.

But the systems themselves continue drifting.

Technical people recognize this immediately because engineers, security analysts, and infrastructure architects spend their lives around environments pretending to be more stable than they actually are.

They develop instincts for hidden instability:
latency spikes,
unusual routing behavior,
privilege escalation,
anomalous traffic,
systems behaving differently under observation.

Human beings leak the same indicators constantly.

Which is why parts of the industry occasionally become impossible to take seriously.

An institution will quietly route a critical infrastructure effort through someone’s technical judgment to validate capability, operationalize the work immediately, redistribute ownership upward through politically safer channels, then months later joke publicly about “vibe coding” as though competence itself were merely a performative illusion.

That’s the real compliance drift.

Not failed paperwork.
Not broken governance controls.
Not missing certifications.

Institutional dishonesty normalized through hierarchy.

Because once institutions become psychologically invested in protecting authority structures, admitting where real capability originated starts feeling more dangerous than the hypocrisy required to deny it.

Compliance culture already has indirect language for behavior like this:
control without attribution.

Large institutions do it constantly.

Extract the value.
Minimize the source.
Redistribute ownership safely through politically survivable channels.
Rewrite the narrative carefully enough that dependency itself disappears from institutional memory.

Which becomes especially absurd in technology because even “vibe coding” still requires enough systems understanding to recognize when the loudest people in the room could not build the infrastructure they are mocking without borrowing someone else’s cognition first.

That is the compliance drift nobody audits:
the growing distance between institutional language and institutional behavior.

Because eventually the person across the table starts conducting an assessment too.

Not on the policies.

On the institution itself.

Which phrases were scripted by legal.
Which concerns originated from leadership panic.
Which questions are fishing expeditions.
Which moments reveal reputational fear rather than ethical concern.
Which parts of the conversation exist purely to manufacture future deniability.

By the middle of the meeting, the audit quietly reverses direction.

The compliance representative believes he is conducting an assessment.

Meanwhile the person across from him has already completed a full behavioral penetration test against the institution itself.

And the findings are rarely encouraging.

Because the real vulnerability inside most institutions is not insufficient governance.

It is the growing gap between:
what the institution claims to value,
what the institution operationally rewards,
and what frightened people inside the hierarchy become willing to rationalize in order to preserve stability.

That gap widens under pressure.

Especially now.

The political climate is unstable.
The technology industry is unstable.
AI acceleration is destabilizing labor structures faster than governance frameworks can adapt.
Entire institutions are quietly terrified they no longer fully understand the systems required to remain competitive.

Fear changes people.

It always has.

And frightened institutions become obsessed with controlling perception because perception feels easier to govern than reality.

That is why modern compliance culture increasingly feels uncanny.

The industry built to prevent institutional corruption occasionally ends up functioning like an advanced linguistic framework for sanitizing it instead.

Not always.
Not everywhere.

But often enough that experienced people recognize the pattern immediately.

Especially the ones who survived enough systems to understand when governance stops protecting human beings and starts protecting institutions from the consequences of human beings instead.

This article is not directed at any specific institution, individual, or technology; it is commentary on broader systemic and organizational dynamics. If certain themes elicit recognition or discomfort, that reflection belongs to the reader, not the author.

I built an open protocol to make AI coding agents follow senior-engineering workflows

AI coding agents are getting better fast, but I kept running into the same failure modes:

  • they skip repository discovery
  • they invent APIs, flags, or config keys
  • they ignore existing project conventions
  • they add unnecessary infrastructure
  • they skip validation
  • they treat unsupported checks as silently done
  • they finish without a final codebase audit

So I built The Polyglot Protocol.

It is an open source senior-engineer workflow protocol for AI coding agents working across polyglot codebases.

Repo:
https://github.com/sabir-gbs/the-polyglot-protocol

What it includes

  • guidance for 22 languages
  • language-selection rules
  • pre-codegen checklists
  • do-not-generate policies
  • validation scripts
  • adapters for Codex, Claude Code, and OpenCode
  • post-codegen audit scoring

The core idea

Before an agent changes code, it should:

  1. inspect the repository
  2. preserve existing conventions
  3. choose the right language and tooling
  4. avoid invented APIs or dependencies
  5. validate the result
  6. document unsupported checks as explicit N/A
  7. finish with a final audit

Model-agnostic by design

I have tested the protocol with frontier models as well as models from Qwen, Grok, Kimi, MiniMax, and others.

The point is not to depend on one model. Strong models still benefit from stricter engineering discipline, and smaller/local models benefit from clearer constraints.

Why open source it?

I want a practical, inspectable protocol that developers can adapt, critique, and improve.

If you use Codex, Claude Code, OpenCode, Cursor, Aider, or similar agents, I’d be interested in feedback:

  • Is the protocol too strict?
  • What guardrails are missing?
  • What should be optional vs required?
  • What would make this easier to adopt?

The Perimeter Economy

Across the technology sector, disputes involving trade secrets, employee mobility, proprietary tooling, side projects, and independently developed systems have intensified dramatically over the last several years. U.S. trade-secret litigation recently reached historic highs as organizations struggled with remote infrastructure, AI development, distributed technical capability, and growing fears surrounding employee mobility and privately controlled systems.

Public disputes involving autonomous systems, semiconductor manufacturing, proprietary algorithms, and advanced infrastructure have exposed something deeper than ordinary intellectual-property conflict. Increasingly, organizations appear anxious not merely about losing data, but about losing proximity to the people capable of creating value independently.

That distinction matters.

Because the modern institutional environment has started developing a strange psychological assumption:

if technical capability exists near the perimeter long enough, eventually it should belong to the perimeter.

Not legally.

Psychologically.

The legal frameworks themselves are comparatively straightforward. Contracts define deliverables. Licensing structures define rights. Privately developed work remains privately developed unless explicitly transferred through enforceable agreements.

Yet culturally, a different logic continues emerging.

A side project built privately begins attracting disproportionate curiosity. Independent experimentation becomes socially visible in strange ways. Technical language starts reappearing through unrelated conversations with almost theatrical consistency. Questions echo through different people. Phrases circulate unnaturally around the environment until the institution itself begins feeling like a system quietly listening to itself think.

Not enough to formally identify coordination.

Just enough to create psychological static.

Recent industry conflicts reveal how widespread this atmosphere has become. The Waymo and Uber litigation surrounding autonomous vehicle systems transformed employee mobility into something resembling strategic containment. Semiconductor firms recently expanded internal monitoring systems and investigations around advanced chip technologies after fears surrounding employee movement and proprietary processes intensified. Entire sectors now increasingly treat highly capable engineers less like employees and more like portable security incidents.

The legal concerns are often real.

The surrounding culture is something else entirely.

Because much of the behavior no longer resembles disciplined technical governance. It resembles anxious ecosystems attempting to perform intelligence work while fundamentally misunderstanding the systems they are attempting to orbit.

And the irony is difficult to ignore:

the people most emotionally invested in technical ownership are frequently the least capable of independently reproducing the systems they obsess over.

That tension sits underneath much of the modern perimeter economy.

Subscribe to the Medium newsletter
The value is sensed socially long before it is comprehended technically.

Ordinary coincidence becomes elevated into strategic interpretation. Shared vocabulary becomes “evidence.” Routine overlap becomes “signals.” Entire conversational ecosystems begin socially mirroring themselves while collectively convincing one another they are conducting sophisticated analysis.

The monitoring itself is often astonishingly unsophisticated.

At times, the atmosphere resembles less a serious analytical culture and more a low-budget performance of institutional paranoia by individuals deeply uncomfortable with the existence of independent technical capability operating outside organizational visibility.

And so softer extraction mechanisms emerge around the perimeter:
ambient monitoring,
behavioral observation,
social mirroring,
reputational ambiguity,
conversation choreography,
persistent curiosity disguised as casual interaction.

No one formally says:
“We own what you create.”

The environment simply becomes emotionally structured around the assumption that boundaries are temporary inefficiencies awaiting erosion.

That is where modern technical culture becomes psychologically revealing. Certain institutional personalities appear unable to tolerate the existence of intellectual life operating independently from organizational appetite. Creativity performed internally is celebrated as innovation. Creativity performed privately becomes culturally suspicious.

Standing near innovation becomes mistaken for participation in innovation.

Observation becomes mistaken for authorship.

Institutional proximity becomes mistaken for technical competence.

And because genuine creation cannot be socially manufactured on demand, some environments gradually drift toward extraction culture instead:
temporary access,
information harvesting,
surveillance theater,
contractor rotation,
reputational pressure,
replacement,
repeat.

A revolving perimeter of people attempting to remain adjacent to capability they cannot independently generate themselves.

At some point, however, every extraction culture encounters the same limitation:

proximity is not competence.

No amount of monitoring, repetition, institutional choreography, or ambient surveillance can permanently substitute for genuine technical authorship.

Because innovation does not emerge from monitoring cultures.

It emerges from the very individuals those cultures so often attempt to absorb.

And perhaps that is the deeper anxiety quietly spreading beneath modern technical ecosystems:

some systems have become so accustomed to acquisition that they no longer remember how creation actually happens.

This article is not directed at any specific institution, individual, or technology; it is commentary on broader systemic and organizational dynamics. If certain themes elicit recognition or discomfort, that reflection belongs to the reader, not the author.