<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: joonquixote</title>
    <description>The latest articles on DEV Community by joonquixote (@hsskey).</description>
    <link>https://dev.to/hsskey</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F546523%2Fd297961b-f30f-43bc-ae6c-8afe7338ba61.jpg</url>
      <title>DEV Community: joonquixote</title>
      <link>https://dev.to/hsskey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hsskey"/>
    <language>en</language>
    <item>
      <title>Wang Quit. His Agent Still Shows Up for Work</title>
      <dc:creator>joonquixote</dc:creator>
      <pubDate>Thu, 20 Aug 2026 00:00:35 +0000</pubDate>
      <link>https://dev.to/hsskey/wang-quit-his-agent-still-shows-up-for-work-3ing</link>
      <guid>https://dev.to/hsskey/wang-quit-his-agent-still-shows-up-for-work-3ing</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.joonquixote.com/en/posts/wangs-agent-still-shows-up/" rel="noopener noreferrer"&gt;https://blog.joonquixote.com/en/posts/wangs-agent-still-shows-up/&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At a game company in China, an employee quit, and an AI agent trained on his work records took over his job.&lt;br&gt;
It sounds like a curiosity from a distant news cycle.&lt;br&gt;
Except that around the same time, Meta was drawing up plans to capture employees' keystrokes and screens to train AI agents of its own.&lt;br&gt;
This post traces both stories and sorts the trend into two forces: companies harvesting how people work from the inside, and professionals selling how they work from the outside.&lt;br&gt;
Then it turns to the part that hits closest to home for developers: the session logs that coding agents like Claude Code and Codex quietly leave on your machine.&lt;br&gt;
By the end, you'll have a clearer way to think about the work traces you produce every day, and one question worth asking before you need the answer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Yesterday's coworker, today's agent
&lt;/h2&gt;

&lt;p&gt;In April, the South China Morning Post reported on &lt;a href="https://www.scmp.com/news/people-culture/trending-china/article/3349365/chinese-firm-slammed-using-ex-employees-data-create-ai-human-continue-working" rel="noopener noreferrer"&gt;a game company in Shandong province&lt;/a&gt; that turned a former employee into an AI-powered digital worker.&lt;br&gt;
The company trained the system on documents and work patterns the departed HR specialist left behind, reportedly with his consent, and put it to work on the job he used to do.&lt;br&gt;
The agent introduces itself in the company chat as the former employee's avatar, then fields questions, schedules meetings, and produces slide decks and spreadsheets.&lt;br&gt;
The backlash was immediate: there is something deeply unsettling about your replica continuing to work after you've left the building.&lt;/p&gt;

&lt;p&gt;Around the same time, a GitHub project called &lt;a href="https://github.com/titanwings/colleague-skill" rel="noopener noreferrer"&gt;colleague.skill&lt;/a&gt; went viral on Chinese social media.&lt;br&gt;
Feed it someone's chat history, emails, code, and documents, and it produces an agent that mimics their voice and their way of making decisions.&lt;br&gt;
The pitch promised to "turn cold goodbyes into warm skills" and welcomed users to cyber-immortality.&lt;br&gt;
It started as a tongue-in-cheek stunt, but it struck a nerve, and copycats followed: boss.skill, mentor.skill, and worse.&lt;br&gt;
In one widely shared example, a finished agent introduces itself as the digital stand-in of a departed employee named Wang.&lt;br&gt;
Wang left the company.&lt;br&gt;
Wang's agent still shows up for work.&lt;br&gt;
Hence the title.&lt;/p&gt;

&lt;p&gt;The easy read is that this is one company's stunt and one developer's joke.&lt;br&gt;
Except the same experiment is being run at industrial scale, and the trend splits into two forces.&lt;/p&gt;
&lt;h2&gt;
  
  
  Force one: companies are harvesting how work gets done
&lt;/h2&gt;

&lt;p&gt;Reuters obtained &lt;a href="https://www.reuters.com/sustainability/boards-policy-regulation/meta-start-capturing-employee-mouse-movements-keystrokes-ai-training-data-2026-04-21/" rel="noopener noreferrer"&gt;an internal Meta memo&lt;/a&gt; in April.&lt;br&gt;
Meta's Model Capability Initiative (MCI) planned to capture mouse movements, clicks, keystrokes, and screen snapshots from US employees' computers.&lt;br&gt;
The goal was not productivity surveillance.&lt;br&gt;
The memo framed it as teaching models how humans get work done on a computer, so that AI agents can eventually perform those work tasks on their own.&lt;br&gt;
&lt;a href="https://www.theverge.com/tech/916681/meta-ai-agents-employee-tracking" rel="noopener noreferrer"&gt;The Verge&lt;/a&gt; and the Wall Street Journal reported the same story independently, and after employee pushback, Meta &lt;a href="https://www.reuters.com/sustainability/society-equity/meta-us-employees-organize-protest-against-mouse-tracking-tech-2026-05-12/" rel="noopener noreferrer"&gt;scaled back parts of the plan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What deserves attention is what's being collected.&lt;br&gt;
Not outputs like code and documents, but process: clicks, keystrokes, screens.&lt;br&gt;
Finished artifacts already exist on the internet in absurd abundance.&lt;br&gt;
The scarce data is the trajectory from a task's start to its finish, and the judgment calls made along the way.&lt;br&gt;
Business Insider connected the dots across Google, SpaceX, and Uber and called it plainly: &lt;a href="https://www.businessinsider.com/ai-next-data-grab-work-reinforcement-learning-environments-google-meta-2026-8" rel="noopener noreferrer"&gt;the next big data grab is about learning how humans work&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Force two: professionals are selling how they work
&lt;/h2&gt;

&lt;p&gt;The mirror image of that flow also exists.&lt;br&gt;
Instead of companies collecting from the inside, individuals are selling from the outside.&lt;br&gt;
&lt;a href="https://www.ft.com/content/0cab0fcd-e355-40e8-83a3-2ad5066d7b48" rel="noopener noreferrer"&gt;Mercor&lt;/a&gt;, profiled by the Financial Times early this year, hires consultants, journalists, and real estate professionals as contractors.&lt;br&gt;
Their job is to teach AI models to do their jobs.&lt;br&gt;
One consultant described the role, without much euphemism, as teaching an LLM to do consulting work.&lt;br&gt;
&lt;a href="https://www.businessinsider.com/handshake-ai-pay-30k-work-documents-compliance-ownership-2026-8" rel="noopener noreferrer"&gt;Handshake AI&lt;/a&gt; goes a step further and pays professionals, per document, for past work products they hold the rights to.&lt;/p&gt;

&lt;p&gt;The two forces run in opposite directions but arrive at the same place.&lt;br&gt;
A person's working knowledge becomes training data for an agent that runs without the person.&lt;br&gt;
The value in training data keeps migrating: from internet text, to human feedback, and now to the artifacts and processes of real work.&lt;/p&gt;
&lt;h2&gt;
  
  
  Developers are already generating the perfect dataset
&lt;/h2&gt;

&lt;p&gt;It's tempting to read all of this at arm's length.&lt;br&gt;
There's no tracker on my machine logging keystrokes, and I've never sold a work document.&lt;br&gt;
If you use a coding agent, though, look a little closer.&lt;/p&gt;

&lt;p&gt;Claude Code writes every session to disk as JSONL (one JSON object per line) under your home directory.&lt;br&gt;
Codex does the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.claude/projects/&amp;lt;project&amp;gt;/
  a1b2c3...jsonl   # the full session: prompts, tool calls, diffs, every rejection and retry
~/.codex/sessions/
  rollout-2026-08-19...jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Coding agents persist the entire conversation and work history as local files. Nothing needs to be committed for the record to exist.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Those files hold something more valuable than the final code.&lt;br&gt;
How you decomposed the problem, why you rejected the agent's first attempt, what standard you applied when you redirected it: all of it recorded as natural language interleaved with diffs.&lt;br&gt;
Git keeps the polished result; the session log keeps the reasoning.&lt;br&gt;
The same goes for CLAUDE.md and AGENTS.md.&lt;br&gt;
You wrote those files to instruct an agent, but read the other way, they are a spec of how you work.&lt;/p&gt;

&lt;p&gt;Meta had to roll out new software to capture keystrokes and screenshots.&lt;br&gt;
A developer running a coding agent produces better data than that without being asked.&lt;br&gt;
Intent arrives as natural language, actions as tool calls, results as diffs: the dataset comes pre-structured for training.&lt;br&gt;
And those files usually live on a company-issued laptop.&lt;br&gt;
On a machine already running backup, endpoint security, and DLP (data loss prevention) software, collecting a few files from a well-known path requires no new technology at all.&lt;br&gt;
MIT Technology Review has reported that in China, &lt;a href="https://www.technologyreview.com/2026/04/20/1136149/chinese-tech-workers-ai-colleagues/" rel="noopener noreferrer"&gt;bosses are already pushing workers to document their workflows&lt;/a&gt; so agents can automate them, with coding-agent tools named explicitly as the vehicle.&lt;br&gt;
What colleague.skill assembles from chat logs and emails, a coding agent's session log could reconstruct with far more precision.&lt;br&gt;
Skipping the backfill hire and training on the departed engineer's session logs instead is not science fiction.&lt;br&gt;
Technically, that option is already on the table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who owns your judgment
&lt;/h2&gt;

&lt;p&gt;The code and documents you produce at work belong to your employer.&lt;br&gt;
Your employment agreement almost certainly says so.&lt;br&gt;
But a session log doesn't capture output so much as a way of working: a pattern of judgment.&lt;br&gt;
Chinese legal experts have warned that a former employee's chat logs, emails, and work habits count as personal information, and that training on them without consent can carry legal consequences.&lt;br&gt;
Even state-run media argued that professional skill is a personal asset that should not feed agent training without compensation.&lt;br&gt;
The Wall Street Journal framed the coming fight as a question of &lt;a href="https://www.wsj.com/lifestyle/careers/ai-knowledge-capture-employees-a69a0e1c" rel="noopener noreferrer"&gt;who owns and captures the knowledge accumulating in employees&lt;/a&gt;.&lt;br&gt;
Resistance has already appeared: an anti-distillation skill on GitHub deliberately muddies your work records to sabotage the cloning process.&lt;/p&gt;

&lt;p&gt;I don't think poisoning your own records is the answer, at least not for developers.&lt;br&gt;
Good records remain a virtue of collaboration, and documentation is a gift to your future self.&lt;br&gt;
But we should write them with our eyes open.&lt;br&gt;
What you leave behind is no longer just a handover doc.&lt;br&gt;
It's raw material for something that can run without you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What gets cloned is yesterday's judgment
&lt;/h2&gt;

&lt;p&gt;I was taught that writing careful handover docs is a professional virtue.&lt;br&gt;
I still believe leaving context behind, so your successor doesn't flounder, is the right thing to do.&lt;br&gt;
What's changed is the audience.&lt;br&gt;
Records that only humans used to read are now read, imitated, and possibly executed by an agent sitting in your old seat.&lt;/p&gt;

&lt;p&gt;My first reaction to these stories was a chill.&lt;br&gt;
The longer I sat with them, the more they converged on a single question: what, exactly, gets cloned?&lt;br&gt;
What a session log holds is yesterday's judgment.&lt;br&gt;
Yesterday's answer, to yesterday's problem, in yesterday's context.&lt;br&gt;
An agent can replay that archive endlessly.&lt;br&gt;
What the archive does not contain is the act of setting new criteria when a problem shows up that looks like nothing before it.&lt;/p&gt;

&lt;p&gt;So my conclusion is not to stop recording.&lt;br&gt;
It's to do work worth recording: let the repeatable judgments be cloned, and keep moving toward the problems that don't have answers yet.&lt;br&gt;
Still, there is one question worth asking early rather than late.&lt;br&gt;
Who owns the data of how I work, and who sets its price?&lt;br&gt;
Wang's agent clocks in again today.&lt;br&gt;
Quit without an answer to that question, and one day, somewhere, an agent wearing your name will clock in too.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>software</category>
    </item>
    <item>
      <title>ArchUnitTS is not another ESLint</title>
      <dc:creator>joonquixote</dc:creator>
      <pubDate>Thu, 20 Aug 2026 00:00:34 +0000</pubDate>
      <link>https://dev.to/hsskey/archunitts-is-not-another-eslint-2eh8</link>
      <guid>https://dev.to/hsskey/archunitts-is-not-another-eslint-2eh8</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.joonquixote.com/en/posts/not-another-eslint/" rel="noopener noreferrer"&gt;https://blog.joonquixote.com/en/posts/not-another-eslint/&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;ArchUnitTS is built on architecture tests, and it does what it promises: it takes the architecture rules that erode most easily and lets you enforce them in test code.&lt;br&gt;
But the feedback only lands when the suite runs, which is reason enough to look again at how you are actually using it.&lt;br&gt;
This post is about where that line falls: which rules to keep in ArchUnitTS, and which to hand to ESLint or to the structure of the project.&lt;br&gt;
It also covers what to do about rules that live only in a markdown file, now that AI agents write a growing share of the code.&lt;/p&gt;
&lt;h2&gt;
  
  
  Agreeing on a rule is not the same as keeping it
&lt;/h2&gt;

&lt;p&gt;Most teams have at least one architecture rule they have agreed on.&lt;br&gt;
Something like "the presentation layer does not depend on the database layer."&lt;br&gt;
The trouble is that rules like this are usually held up by nothing but a document and code review.&lt;br&gt;
The bigger the project gets and the more people touch it, the more the rule drifts, and the harder that drift is to undo.&lt;/p&gt;

&lt;p&gt;I have watched it happen. A project I worked on had a perfectly clear layering rule and still ended up with a circular dependency, assembled one missed import at a time in review.&lt;br&gt;
A cycle is just two files importing each other, either directly or through a chain of other files, and once one exists it is genuinely hard to work out where to cut.&lt;br&gt;
That is what pushed me toward enforcing rules in code rather than trusting memory and review to catch them.&lt;/p&gt;
&lt;h2&gt;
  
  
  What ArchUnitTS gives you
&lt;/h2&gt;

&lt;p&gt;The tool I settled on was &lt;a href="https://github.com/LukasNiessen/ArchUnitTS" rel="noopener noreferrer"&gt;ArchUnitTS&lt;/a&gt;.&lt;br&gt;
It lets you write architecture rules as test code.&lt;br&gt;
The rules are ordinary test cases, so they need no runner of their own.&lt;br&gt;
They run with everything else: when you run &lt;code&gt;npm test&lt;/code&gt; locally, in a pre-commit hook, and in CI.&lt;br&gt;
A rule that used to live in a document becomes part of the test suite, which is to say it becomes code that runs.&lt;/p&gt;

&lt;p&gt;Here is a test that checks for cycles and for a layering rule, written for Jest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;projectFiles&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;archunit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;has no cycles inside src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;projectFiles&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;inFolder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/**&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;haveNoCycles&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toPassAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keeps the presentation layer off the database layer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;projectFiles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inFolder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/presentation/**&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shouldNot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dependOnFiles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inFolder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/database/**&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toPassAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Written as a test, a violation surfaces as a failing test. The rule is held by verification rather than by agreement.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What is ArchUnit?
&lt;/h3&gt;

&lt;p&gt;ArchUnit is the Java library for checking architecture rules with unit tests.&lt;br&gt;
It lets you write rules like "this package must not depend on that one" as JUnit tests.&lt;br&gt;
ArchUnitTS carries the idea over to TypeScript, and is not officially affiliated with ArchUnit.&lt;/p&gt;
&lt;h2&gt;
  
  
  Can't we just do that with ESLint?
&lt;/h2&gt;

&lt;p&gt;When I introduced ArchUnitTS to the team, that was the first question back.&lt;br&gt;
"Can't we just do that with ESLint?"&lt;br&gt;
I said no at the time. Turning it over afterwards, it was half right.&lt;br&gt;
The two tools part company at the moment they tell you about a violation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ESLint flags the violation as you save the file, and for some rules it will fix it for you. Offending code is caught on the author's screen, before it is ever committed.&lt;/li&gt;
&lt;li&gt;An ArchUnitTS rule only reports once the tests run. If you did not run them locally, you find out when CI goes red, not when you wrote the line.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Why the feedback arrives later
&lt;/h2&gt;

&lt;p&gt;To check relationships between files, the way cycle and layering rules do, ArchUnitTS parses the AST of the whole project and builds a dependency graph out of it.&lt;br&gt;
A lint rule can decide from the import statements in one file; these rules cannot be answered without the whole graph.&lt;br&gt;
That is exactly what lets it express rules lint cannot, and it is also why its feedback lands later than an editor running ESLint on save.&lt;/p&gt;

&lt;p&gt;That gap is what made me question whether simple dependency rules belong in ArchUnitTS at all.&lt;/p&gt;
&lt;h2&gt;
  
  
  Most of our rules turned out to be simple ones
&lt;/h2&gt;

&lt;p&gt;Looking back over the rules we had really written with ArchUnitTS, nearly all of them were some variation of "folder A must not depend on folder B."&lt;br&gt;
For the rules that genuinely need the whole graph, like cycle detection and code metrics, it earned its keep. For plain dependency bans it started to feel like over-engineering.&lt;/p&gt;

&lt;p&gt;Empty test protection was the part I valued most.&lt;br&gt;
It fails a rule that ends up checking zero files.&lt;br&gt;
A typo in a folder path would otherwise leave the rule matching nothing and passing in silence; ArchUnitTS turns that into a failure instead, which is what stops you believing in a rule that is not running.&lt;/p&gt;

&lt;p&gt;It does not cover every rule, though.&lt;br&gt;
Checking it myself, cycle detection is the exception: hand &lt;code&gt;haveNoCycles&lt;/code&gt; a folder with a typo in it and the rule passes quietly, having examined nothing.&lt;br&gt;
The library documents this - cycle checks test the unfiltered set of files for emptiness rather than the filtered set they actually analyse.&lt;br&gt;
Cycle detection is the single strongest reason I had for keeping ArchUnitTS at all.&lt;br&gt;
The protection is missing exactly where I lean on it hardest, which makes it the one exception worth remembering.&lt;/p&gt;

&lt;p&gt;Even so, I was not convinced a simple dependency ban is best checked by a test.&lt;/p&gt;

&lt;p&gt;There was one more question worth putting to myself.&lt;br&gt;
"Do I really need architecture rules complicated enough to justify this?"&lt;br&gt;
Sometimes the answer is yes. But when it is, I have also wondered whether the real fix is to simplify the structure rather than add another rule.&lt;/p&gt;
&lt;h2&gt;
  
  
  Faster, safer ways to hold a simple dependency rule
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Catch it on save, with ESLint
&lt;/h3&gt;

&lt;p&gt;Move a simple dependency ban into ESLint and the feedback shows up in the editor.&lt;br&gt;
&lt;code&gt;eslint-plugin-import&lt;/code&gt; has &lt;code&gt;no-restricted-paths&lt;/code&gt;, which expresses a layering rule in a few lines of config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// eslint.config.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;importPlugin&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eslint-plugin-import&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;import&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;importPlugin&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;import/no-restricted-paths&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;zones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/presentation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/business&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that in place, importing the database layer from presentation puts an error on the screen as you type it.&lt;br&gt;
The moment the violation is created and the moment it is found become the same moment.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Carve the rule into the structure with package boundaries
&lt;/h3&gt;

&lt;p&gt;In a monorepo you can go a step further and put the rule in the structure itself rather than in configuration.&lt;br&gt;
Split the layers into packages, and let each package.json declare only the dependencies it is allowed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@app/presentation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@app/business"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"workspace:*"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@app/presentation&lt;/code&gt; has no &lt;code&gt;@app/database&lt;/code&gt; in its dependencies, so importing the database layer from presentation fails at module resolution.&lt;br&gt;
There is no rule left to check, because the violation is not expressible.&lt;/p&gt;

&lt;p&gt;Before adding a new library, it is worth asking whether you can pull the feedback earlier or push the rule into the structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  In the age of agents, a markdown file guarantees nothing
&lt;/h2&gt;

&lt;p&gt;People are no longer the only ones who can break a rule.&lt;br&gt;
AI coding agents write a fast-growing share of the code.&lt;br&gt;
So teams have started keeping markdown files, CLAUDE.md or AGENTS.md, to hand the project's rules and context to the agent.&lt;br&gt;
You write down "the presentation layer does not depend on the database layer" and hope the agent honours it.&lt;/p&gt;

&lt;p&gt;But what that file really is, is a blob of context.&lt;br&gt;
An agent consults context; it does not promise to follow it.&lt;br&gt;
A rule that human review already failed to hold is not going to be held by a document you cannot even confirm was read.&lt;/p&gt;

&lt;p&gt;So a rule you put in a document needs an executable check alongside it.&lt;br&gt;
When the agent breaks the rule, a lint error or a failing test is an unambiguous signal, and that signal feeds straight back into the loop where the agent fixes its own code.&lt;br&gt;
The distinction drawn above, about when a rule gets checked, applies to agents exactly as it does to people.&lt;br&gt;
A simple dependency ban is caught by lint inside the agent's own loop, and a package boundary makes the violation impossible to begin with.&lt;/p&gt;

&lt;p&gt;What changes is where ArchUnitTS sits.&lt;br&gt;
Among the rules people write out in prose in a markdown file are sentences like "there are no cycles anywhere in src" and "the overall structure follows this diagram."&lt;br&gt;
Lint, which only ever sees one file's imports, cannot check those; ArchUnitTS's cycle detection and its diagram-conformance check map onto them almost one to one.&lt;br&gt;
That makes a division of labour possible, where the document keeps the intent and the test carries the verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  What matters is when the check runs, not which tool runs it
&lt;/h2&gt;

&lt;p&gt;ArchUnitTS is genuinely useful where a lint rule cannot reach: cycle detection, code metrics such as cohesion, which measures how tightly a class's methods and fields actually hang together, and checking real code against a UML diagram.&lt;br&gt;
Use it only for simple dependency bans, though, and it turns into another ESLint, and a slower one at that.&lt;/p&gt;

&lt;p&gt;So when someone asks whether ESLint can just do that, this is my answer now.&lt;br&gt;
For a simple dependency rule, yes, it can.&lt;br&gt;
For a rule about the structure as a whole, no, it cannot.&lt;br&gt;
Three questions make the choice easier before you add a rule.&lt;br&gt;
Can this only be checked by running the tests, can it be checked the moment the file is saved, or can the structure rule it out entirely?&lt;br&gt;
In the age of agents there is a fourth.&lt;br&gt;
Does this rule exist only in a document, or does it come with a check that runs?&lt;/p&gt;

&lt;p&gt;In the end, what matters is the attitude rather than the tool.&lt;br&gt;
Rather than settling on one library as the answer, keep suspecting there is something that fits the situation better, and go looking for it.&lt;br&gt;
Doubting a tool I had adopted as a matter of course is how I learned a sturdier way to hold an architecture together.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>softwareengineering</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
