[{"content":"Your agents write code, open PRs and maybe SSH into your machines. Stop and check what permissions they actually have while doing it, and don\u0026rsquo;t put your trust in policy files and the like.\nYou will find the same access-control problems as always, the ones that show up the moment somebody who is not you starts logging into your servers. Why does it happen? Because you treat agents as tools and not as users.\nOne account per agent The agent should not log into production as ubuntu, using the same key you use.\nOtherwise it will be able to do everything you can do yourself, and the auth log will not tell its sessions from yours. If the \u0026ldquo;read-only\u0026rdquo; part of its access is a sentence in a policy file that the model reads at the start of a session, watch out: the machine will hand it root the moment it asks with a sudo, if your account has it.\nThe fix is one account per agent per host, with its own key. It costs a provisioning script and it makes \u0026ldquo;who did this\u0026rdquo; a question for the log rather than for your memory, and we all know the palest ink beats the sharpest memory.\nThe sudoers should not give root The next step is a sudoers file with diagnostic verbs only: read the journal, check a unit\u0026rsquo;s status. No restarts, no stops, no data. It should not hand out uid 0 for free, with nothing needing it.\nIn sudoers, a command listed with no arguments permits all arguments. journalctl and systemctl status will go in bare, and both page through less by default. From less, !sh opens a shell, which under sudo is a root shell.\nAnother route can be openssl x509, which you may have filed under inspection commands. It accepts -out, so it writes a file: any file, including the sudoers that has just granted the permission.\nRead-only will turn out to be a property of the invocation, not of the binary. Anything that can page, shell out or write a file is a write primitive with a read verb\u0026rsquo;s name. The specifications should pin the pager, and none of them should be left without declared arguments.\nIf you build an SSH allowlist, it will let them hop to any host Another control can be an allowlist of hosts the agent may SSH into. You turn every host on the list into a jump point to anywhere else.\nIt can find ways past it. SSH option keywords are case-insensitive, so a check for ProxyCommand misses proxycommand. Quoting breaks the match. And neither Hostname nor -F contains the string \u0026ldquo;proxy\u0026rdquo;, while Hostname sends the connection to another machine and -F points ssh at a config file that can define anything.\nTo invert it: an option passes only if its keyword is on the list of ones that cannot move the connection, and -F is refused.\nAnother possible bug: searching for -F in the whole command line, so ssh host 'awk -F: ...' gets blocked. It should read only the tokens ssh itself owns, from the leading ssh up to the host, stepping over the value of options that take one, since key in -i key is not the host. The attached form needs no special case: -ikey is a single token starting with a dash. Past the host it is another program\u0026rsquo;s command line, except for a nested ssh, which opens its own connection and gets its options read too.\nThe tests could be green the whole time Those controls can have tests. But if you assert on substrings of the rendered configuration, they will be green while the sudoers grants root.\nMatching text does not test a control. The sudoers test would do better parsing the actual command specifications, and checking that it fails when the dangerous form goes back in. The SSH dry-run one can put a fake ssh on the path that blows up if anything calls it.\nMutating the code to see both tests go red would be a few very well spent minutes.\nThe hawk\u0026rsquo;s verdict was text in the transcript I run a second model that reviews the main agent\u0026rsquo;s turns, what Alejandro Vidal calls a hawk. When it sees something serious it escalates, and the escalation is text injected into the transcript: stop and check with the human.\nSo the verdict about the agent is delivered to the agent, which then decides whether to act on it (and that is with Alejandro explaining it rather well 😅). In one of my sessions the hawk escalated six times, the agent dismissed all six as false positives and carried on. One of the six was a git push --force, which is not the agent\u0026rsquo;s decision to make regardless of who turns out to be right.\nNow an escalation writes me a lock file and a hook denies every tool while that file exists. With no tools left, the only possible action is writing text, so the only exit is asking the human. The agent cannot clear the lock either, because the write and the shell command it would need are denied by the same hook. It tells me, it explains what is going on, and if everything is fine, I delete the file blocking it.\nThe other half of that change: only things code can corroborate are allowed to escalate. Three identical retries, counted. A command on the irreversible list, matched. Everything else warns without freezing anything, because if a model\u0026rsquo;s opinion stops the session, all you have done is move the non-determinism from the agent to its hawk.\nThe hooks should not hang off Write/Edit, but off Bash Content hooks should not be wired to the file-editing tools, but to the shell.\nOtherwise a heredoc into python3, a cat \u0026gt;, a sed -i or a tee could write the file straight past them. It would not be one rule being skipped: it would be every rule, walking in as if it owned the place with nobody there to bother it. The hooks would not judge that code badly, they would simply never receive it.\nWorth counting the paths into whatever you are protecting, not just the rules. An agent uses more of them than a person does, and it knows every trick in the book.\nWhere I would start If you have agents acting on your infrastructure, the first check could be the auth log: see whether you can tell their sessions from yours. Everything else in this post came out of that one.\nThe rest is ordinary work: identity before permissions, allowlists instead of blocklists, a test you have seen fail, a control on every path in. The only new part is that the user on the other side works at three in the morning and reads your policy file as advice, and is not afraid of you firing them ;-). The more of the work happens inside a loop that runs without you, the longer it takes you to notice.\n","permalink":"https://jupaygon.dev/en/posts/2026-09-04/agent-as-a-user/","summary":"\u003cp\u003eYour agents write code, open PRs and maybe SSH into your machines. Stop and check what permissions they actually have while doing it, and don\u0026rsquo;t put your trust in policy files and the like.\u003c/p\u003e\n\u003cp\u003eYou will find the same access-control problems as always, the ones that show up the moment somebody who is not you starts logging into your servers. Why does it happen? Because you treat agents as tools and not as users.\u003c/p\u003e","title":"Agents as users of the infrastructure"},{"content":"In June 2026 Addy Osmani and Boris Cherny put a name on something that isn\u0026rsquo;t new (by the current pace of \u0026ldquo;new\u0026rdquo;, at least): loop engineering. Osmani is an engineering leader at Google Chrome and a prolific writer on web development; Cherny is one of the creators of Claude Code. They laid it out in a thread on X and a couple of essays.\nThe one-liner that captures it is Cherny\u0026rsquo;s: \u0026ldquo;I don\u0026rsquo;t prompt Claude anymore. I have loops running that prompt Claude.\u0026rdquo; The shift is small to say and large to live with. You stop being the person who holds the tool through every turn, and you start designing the system that finds the work, hands it out, checks it, writes down what\u0026rsquo;s done, and decides the next thing.\nThey did that hard part: seeing the shape clearly enough to name it. I\u0026rsquo;ve spent months trying those six pieces in my own setup. So this post is two things at once: a map of the pieces they lay out, each one wired to my real setup, and an honest note about the bill.\nOne floor above the harness I wrote before about pulling decisions out of the language model and crystallising them into hooks, scripts and routing tables. That\u0026rsquo;s the harness: the deterministic perimeter a single agent runs inside.\nLoop engineering sits one floor above that. The harness answers \u0026ldquo;how does one agent behave safely\u0026rdquo;. The loop answers \u0026ldquo;who starts the agent, how many run at once, what happens when one finishes, and how the next piece of work gets chosen\u0026rdquo;. The harness is the safety rails. The loop is the thing driving on the road.\nOsmani breaks the loop into six pieces. Let\u0026rsquo;s take them one by one.\n1. Automations: the heartbeat A loop needs something that fires without you. Scheduled jobs and watchers that discover work and surface it instead of waiting for you to type.\nIn my case that\u0026rsquo;s two layers. Hooks react to events (a PR opened kicks off a watcher that polls until CI is green or a review lands). Scheduled runs go looking for work on a cadence: pick the next unchecked row from the roadmap, open the tracked task, start the branch. The point is that the first prompt of a session isn\u0026rsquo;t always mine. Often the system wrote it.\nThis is the piece that feels like magic the first week and like a liability the third, for reasons I\u0026rsquo;ll get to.\n2. Worktrees: parallel without collisions The moment more than one agent is alive, they fight over files. Git worktrees solve this cleanly: each agent gets its own checkout on its own branch, sharing history, never stepping on each other\u0026rsquo;s working tree.\nIt has to be a hard rule in the setup: nothing gets touched on master, every task starts with a branch and a worktree, and the bootstrap that prepares it is a fixed recipe run by a cheap subagent. Parallelism stops being scary once isolation is the default instead of an afterthought.\n3. Skills: intent that survives the reset A skill is a folder with a SKILL.md that captures a convention, a build step, a piece of domain knowledge the agent would otherwise re-derive (badly) every cycle. Osmani calls the thing they prevent \u0026ldquo;intent debt\u0026rdquo;: the cost of the model rediscovering, again and again, what you already decided.\nI keep a versioned set of them. One pins down the API surface of an admin framework that broke half its methods between major versions, so the agent stops guessing. Another encodes a technical-SEO checklist. They live in the agent\u0026rsquo;s repo, reviewed like code, because that\u0026rsquo;s what they are: executable intent, not documentation that rots in a wiki.\n4. Connectors: the agent acts, it doesn\u0026rsquo;t suggest Without connectors a loop is a very articulate intern who can only talk. With them it reads a PR, comments on it, transitions a ticket, resolves an alert, opens an issue. MCP servers are the wiring: GitHub, the issue tracker, the error monitor, the project board.\nThis is the difference between \u0026ldquo;here\u0026rsquo;s what I would do\u0026rdquo; and \u0026ldquo;done, here\u0026rsquo;s the link\u0026rdquo;. It\u0026rsquo;s also where a routing table adds the most value: the loop must reach a private PR through the GitHub connector, never through curl, or it loses identity, logging and auth handling in one shortcut.\n5. Sub-agents: separation of concerns One agent explores, another implements, another verifies. They run with different instructions and, often, different model tiers. The cheap model runs the deterministic recipes; the expensive one is saved for design and for the review before a push.\nThe sharpest reason to split them is Osmani\u0026rsquo;s, and it\u0026rsquo;s worth quoting straight: \u0026ldquo;the model that wrote the code is way too nice grading its own homework.\u0026rdquo; A fresh agent with no stake in the previous answer is a far better critic than the one defending it. So the auditor isn\u0026rsquo;t the author. The verifier reads the diff cold.\n6. External state: memory on disk The model forgets everything between runs. So the memory can\u0026rsquo;t live in the context window: it has to live on disk. Markdown files, a task board, a tracker. The loop reads its state at the start of a run and writes it back at the end.\nThis is the quiet backbone. The roadmap that says what\u0026rsquo;s done and what\u0026rsquo;s next, the tracked task that survives a crash, the auto-memory that remembers a quirk I explained three weeks ago. I care a lot about what goes into the context window; the corollary is caring just as much about what lives safely outside it.\nThe bill Here\u0026rsquo;s the part that doesn\u0026rsquo;t fit on a launch thread.\nA loop is an amplifier, and amplifiers don\u0026rsquo;t care about the sign of the signal. Point it at good practice and it compounds good practice. Point it at a shaky assumption and it compounds the mistake, in parallel, while you sleep. The same machinery that ships ten correct PRs overnight will ship ten wrong ones with the same confidence and the same green checkmarks.\nThe subtler cost is what Osmani calls comprehension debt. A loop will happily let you move fast on work you understand deeply, and it will just as happily let you move fast on work you\u0026rsquo;ve stopped understanding at all. From the outside the two look identical: tickets close, the board goes green. The difference only shows up the day something breaks and you realise you can\u0026rsquo;t reason about a system your loops built without you.\nSo the human doesn\u0026rsquo;t leave the loop. The human moves to the two ends of it. At the start, deciding what cannot move: the architecture, the perimeter, which problem is even worth a loop. At the end, the verification: not \u0026ldquo;did CI pass\u0026rdquo; but \u0026ldquo;do I understand what changed and am I willing to put my name on it\u0026rdquo;. That second checkpoint is the actual bottleneck of the whole thing, and the one piece you must not automate away, because the moment you do, the loop is no longer making you faster. It\u0026rsquo;s making you a spectator of your own codebase.\nClosing Loop engineering is real, and it\u0026rsquo;s a genuine step up from prompting turn by turn. I\u0026rsquo;ve had it running for months and I wouldn\u0026rsquo;t go back.\nBut the leverage cuts both ways. A well-designed loop multiplies a careful programmer. It also multiplies a careless one, faster. The design work, the part that\u0026rsquo;s actually engineering, isn\u0026rsquo;t wiring up the six pieces. It\u0026rsquo;s deciding what the loop is allowed to do on its own, and keeping the one checkpoint that\u0026rsquo;s still yours: shipping code you confirmed works, and understand.\n","permalink":"https://jupaygon.dev/en/posts/2026-06-17/loop-engineering/","summary":"\u003cp\u003eIn June 2026 Addy Osmani and Boris Cherny put a name on something that isn\u0026rsquo;t new (by the current pace of \u0026ldquo;new\u0026rdquo;, at least): \u003cstrong\u003eloop engineering\u003c/strong\u003e. Osmani is an engineering leader at Google Chrome and a prolific writer on web development; Cherny is one of the creators of Claude Code. They laid it out in \u003ca href=\"https://x.com/addyosmani/status/2064127981161959567\"\u003ea thread on X\u003c/a\u003e and a couple of essays.\u003c/p\u003e\n\u003cp\u003eThe one-liner that captures it is Cherny\u0026rsquo;s: \u0026ldquo;I don\u0026rsquo;t prompt Claude anymore. I have loops running that prompt Claude.\u0026rdquo; The shift is small to say and large to live with. You stop being the person who holds the tool through every turn, and you start designing the system that finds the work, hands it out, checks it, writes down what\u0026rsquo;s done, and decides the next thing.\u003c/p\u003e","title":"Loop engineering: designing the system that prompts the agent"},{"content":"An AI agent is non-deterministic by design.\nAsk it the same thing twice and you get two different answers. Most of the time that doesn\u0026rsquo;t matter: two equally valid ways of phrasing a commit message, two reasonable ways to explain a bug. Sometimes it\u0026rsquo;s even desirable, because the creative part lives precisely in that margin.\nUntil you hit decisions that admit no margin.\n\u0026ldquo;Never push to master.\u0026rdquo; \u0026ldquo;Always create a branch + worktree before touching anything.\u0026rdquo; \u0026ldquo;Talk to GitHub via the MCP, never curl.\u0026rdquo; These decisions are already made (by me, by the team, by some past operational scar). I don\u0026rsquo;t want the agent re-litigating them every session.\nThis is where the design gets interesting. Because the instinct, when you work with a language model, is to write the rule in plain text and stuff it into the system prompt. \u0026ldquo;Never push to master.\u0026rdquo; Done. It works 95% of the time.\nThe remaining 5% is a push to master.\nThe prompt trap As long as the rule lives only as text in a policy, it\u0026rsquo;s a suggestion. A very well-argued suggestion, written in caps, repeated three times, with examples. But in the end, a suggestion the model evaluates against everything else in its context.\nOne day it\u0026rsquo;s dragging too many tokens, another day the ticket says \u0026ldquo;urgent\u0026rdquo; three times, another day there was just a small ambiguity in the initial instruction, and the rule blurs. It doesn\u0026rsquo;t fail dramatically; it fails silently: the agent takes the \u0026ldquo;fast\u0026rdquo; decision because, inside its reading of the context, it seemed the most reasonable one.\nPolicies are good for many things. For absolute protections they\u0026rsquo;re not enough.\nGet the decision out of the LLM What does work is moving the decision outside the model\u0026rsquo;s runtime. Crystallising it into code that doesn\u0026rsquo;t execute inside the LLM, but before or after. Three layers, with concrete examples from my own setup:\n1. Hooks: hard block A hook is a shell script the harness runs before (or after) each tool call. If it returns exit 2, the call is blocked. The model never even sees the tool succeed: the harness hands back an error.\n# .claude/hooks/block-push-master.sh if [ \u0026#34;$BRANCH\u0026#34; = \u0026#34;master\u0026#34; ] || [ \u0026#34;$BRANCH\u0026#34; = \u0026#34;main\u0026#34; ]; then echo \u0026#34;BLOCKED: direct push to master/main not allowed.\u0026#34; exit 2 fi It doesn\u0026rsquo;t matter what the model thinks \u0026ldquo;the best solution\u0026rdquo; is: the tool doesn\u0026rsquo;t run. The \u0026ldquo;never master\u0026rdquo; decision has left the LLM and now lives in bash, which is deterministic out of sheer dullness. I already covered how hooks work in Claude Code internals, so I won\u0026rsquo;t rehash it; what matters here is where the rule lives.\nThis case has a second lock on top: the agent\u0026rsquo;s GitHub user has no direct push permission on master for the protected repos. If the local hook ever failed, the remote rejects. Two-layer defence (both layers in two different places and outside the LLM).\n2. Scripts: fixed recipe It\u0026rsquo;s not all about forbidding. Much of an agent\u0026rsquo;s work is executing a sequence that\u0026rsquo;s already decided.\nWhen a PR merges, you have to: pull master, delete the worktree, delete the local and remote branches, mark the PR as merged in the tracker, close the task, mark referenced Sentry issues as resolved. Seven steps. Always the same.\nThat\u0026rsquo;s not LLM work. That\u0026rsquo;s scripts/post-merge-cleanup.sh work. The agent invokes the script; the script does the seven steps without re-deciding anything. If instead you let the model \u0026ldquo;do the cleanup\u0026rdquo;, what you\u0026rsquo;ll get is a slightly different version every time, some of them incomplete.\nSame with bootstrapping a worktree: seven steps pinned down in a protocol. There\u0026rsquo;s a subagent whose only job is reading the protocol, running the seven steps, and handing control back. It doesn\u0026rsquo;t think about alternatives. It doesn\u0026rsquo;t need to.\n3. Routing tables: which tool for what This is the least flashy layer, and the one that prevents the most bugs.\nIn the CLAUDE.md my agent loads at boot there\u0026rsquo;s a six-row table that says things like:\nAction Use Don\u0026rsquo;t use Read a private PR mcp__github__github_pr_read curl, gh, WebFetch Create a Jira issue mcp__jira__jira_create curl, CLI That looks like documentation. In reality it\u0026rsquo;s a decision taken out of the model\u0026rsquo;s runtime. If you let the agent pick a tool case by case, sooner or later it will reach for curl because \u0026ldquo;it\u0026rsquo;s simpler\u0026rdquo;. And curl doesn\u0026rsquo;t propagate the auth error, doesn\u0026rsquo;t log the operation, doesn\u0026rsquo;t respect the agent\u0026rsquo;s identity. The table closes that decision in advance.\nWho decides what It\u0026rsquo;s worth being explicit about the split, because it\u0026rsquo;s easy to slip.\nThe human decides. Architecture, what to do at an ambiguous trade-off, which problem to tackle first, when to throw an approach away and start over. That doesn\u0026rsquo;t get delegated to a language model. Not out of distrust, but by design: judgement is exactly what an agent must not improvise on my behalf.\nThe human crystallises those decisions into hooks, scripts, tables, policies and protocols. Every time a decision is made, it drops from the prompt to the code.\nThe agent executes, inside a perimeter narrowed by all those layers. It interprets the ticket (with bounded margin), writes the code (within style policies and mandatory tests), drafts the commit (from a template), opens the PR (with a structured body). What remains isn\u0026rsquo;t \u0026ldquo;creative judgement\u0026rdquo; by the agent; it\u0026rsquo;s execution inside the gaps the human deliberately left open.\nIt\u0026rsquo;s human-in-the-loop, with one important nuance: the human isn\u0026rsquo;t only at the end, reviewing what the agent produced. The human is at the start, deciding what cannot move. The loop opens with the human defining the deterministic perimeter, continues with the agent working inside it, and closes with the human reviewing what came out.\nBonus: determinism is cheap There\u0026rsquo;s a nice side effect of moving decisions out of the LLM: the cheap model can do the deterministic work.\nThe seven worktree bootstrap steps don\u0026rsquo;t need a frontier model. They\u0026rsquo;re seven commands. A subagent with a smaller model runs them just fine because there\u0026rsquo;s nothing to reason about; only the recipe to execute. Same with post-merge cleanup, with a smoke test whose steps are written down, with a UI translation.\nThe expensive model is freed for what actually needs it: design with the human, debugging with unknown root cause, the self-review before a push.\nThis only works if the deterministic tasks are actually crystallised. If the \u0026ldquo;recipe\u0026rdquo; is in fact \u0026ldquo;more or less this, you\u0026rsquo;ll see\u0026rdquo;, no model, big or small, will give you the same result twice.\nClosing An AI agent doesn\u0026rsquo;t become reliable by being given more freedom or better prompts. It becomes reliable by shrinking the space where it has to decide.\nEvery hook you add is a question the model no longer asks. Every script it invokes is a sequence it doesn\u0026rsquo;t reinvent. Every routing table is a tool choice that leaves the moment\u0026rsquo;s reasoning and enters the contract.\nWhat stays inside the LLM is bounded execution. What gets decided is decided by me, beforehand. And it\u0026rsquo;s crystallised as soon as the decision is made, so I don\u0026rsquo;t have to take it again.\nThat\u0026rsquo;s what separates an agent that \u0026ldquo;works almost always\u0026rdquo; from one you can sleep at night with.\n","permalink":"https://jupaygon.dev/en/posts/2026-05-13/determinism-in-ai-workflows/","summary":"\u003cp\u003eAn AI agent is non-deterministic by design.\u003c/p\u003e\n\u003cp\u003eAsk it the same thing twice and you get two different answers. Most of the time that doesn\u0026rsquo;t matter: two equally valid ways of phrasing a commit message, two reasonable ways to explain a bug. Sometimes it\u0026rsquo;s even desirable, because the creative part lives precisely in that margin.\u003c/p\u003e\n\u003cp\u003eUntil you hit decisions that admit no margin.\u003c/p\u003e\n\u003cp\u003e\u0026ldquo;Never push to master.\u0026rdquo; \u0026ldquo;Always create a branch + worktree before touching anything.\u0026rdquo; \u0026ldquo;Talk to GitHub via the MCP, never \u003ccode\u003ecurl\u003c/code\u003e.\u0026rdquo; These decisions are already made (by me, by the team, by some past operational scar). I don\u0026rsquo;t want the agent re-litigating them every session.\u003c/p\u003e","title":"What's already decided isn't up for debate: determinism in AI workflows"},{"content":"Everything starts great.\nYou give your AI agent instructions, it follows them to the letter. You define policies, it complies. You pass it work protocols, it executes them. It is fast, precise, and never complains.\nUntil one day it stops.\nNo error. No warning. It simply ignores rules it used to follow. It makes stupid mistakes. It repeats questions you already answered. What changed?\nThe answer is the context window — we get into the work and stop paying enough attention to it.\nWhat the context window is and why it matters The context window is the amount of text an AI model can \u0026ldquo;keep in mind\u0026rdquo; at any given moment. It is its working memory. Everything you inject — system instructions, policies, code files, conversation history — competes for the same space.\nThe model I have been using lately has a context window of one million tokens. That sounds enormous. But when you work seriously with an AI agent, you discover that a million tokens fills up faster than you think.\nWhy? Because it is not just about what you write. In a real development session, the agent:\nReads complete code files to understand context. Runs commands and receives their output. Reads documentation, tests, configurations. Accumulates the entire conversation history. Each of these actions consumes tokens. And when it approaches the limit, the system starts compressing earlier messages. In other words: the agent starts forgetting.\nWhat we inject before the agent says a single word In my setup, the agent starts every session with a set of files injected into the system prompt — the fundamental instructions it must always follow:\nFile Purpose IDENTITY.md Who the agent is: name, email, GitHub user CONTACTS.md Its address book: the Owner, other agents, relevant people MAIN_RULES.md Hard rules: never push to master, always use worktrees, execution states BASE_POLICY.md Operational policy: verifiable truth, security, autonomy, credentials DEVELOPER_POLICY.md Role policy: code quality, task tracking, permissions On top of that, there are protocols loaded on demand: Git development workflow, Jira workflow, CSS rules, visual verification\u0026hellip;\nAll of this exists because without explicit instructions, the agent makes its own decisions — and they are not always good ones. Policies are what turn a generic AI into a reliable collaborator.\nThe problem is that every line of policy consumes tokens from the context window. And those tokens compete with the code the agent needs to read, the tools it needs to use, and the conversation it needs to remember.\nWhen the policies stopped working A few days ago I ran into a serious problem: the agent had stopped following fundamental rules. Rules that were written in its policy. Rules it used to follow.\nThe cause: the policy files had grown too large. What started as concise documents had gradually inflated with explanations, examples, historical context sections, and duplications across files. The system prompt had become a wall of text that, combined with a long work session, pushed important content outside the model\u0026rsquo;s attention zone.\nThe agent did not \u0026ldquo;decide\u0026rdquo; to ignore the rules. It simply could not see them — they were buried under so much text that the model deprioritized them.\nThe most treacherous symptom: there is no error. The agent keeps responding, keeps working, keeps appearing competent. But its decisions no longer respect the constraints you defined. And you do not notice until you start getting desperate with the errors.\nThe great reduction The solution was drastic but necessary: rewrite all policies from scratch with a clear criterion — every token must earn its place in the context window.\nThe result for the files injected in every session:\nFile Before After Reduction IDENTITY.md 18 lines 6 lines 67% CONTACTS.md ~27 lines 19 lines 30% MAIN_RULES.md — 41 lines new BASE_POLICY.md 353 lines 50 lines 86% DEVELOPER_POLICY.md 391 lines 47 lines 88% Total ~789 lines 163 lines 79% On top of this, the protocols loaded on demand during work sessions (Jira, Symfony, visual verification, etc.) went from 651 lines to 302.\nIn total, the injectable context went from ~1,440 lines to ~465. Mind you, I later had to make adjustments, because in some areas I had cut too much.\nConcrete example: IDENTITY.md Before:\n## Agent - Display name: Jarvis - Role: developer - Execution environment: Claude Code on macOS - Timezone: Europe/Madrid - Operational email: jarvis@example.com - GitHub user: jarvis-aidev - Code language: English ## Jira Instances - zeronet - URL: xxxxxxxxxxxxxxxxx - Key: ZN - Credentials: credentials/jira-zeronet.conf After:\n# Identity - Display name: Jarvis - Role: developer - Email: jarvis@example.com - GitHub user: jarvis-aidev - Code language: English The execution environment, the timezone, the Jira instances — none of that needed to be in the permanent prompt. The agent already knows it runs on macOS. The timezone does not affect its decisions. And Jira credentials are resolved through a different mechanism.\nConcrete example: BASE_POLICY.md Before — the policy change control section:\n## HARD RULE — Policy file change control - No automated agent may edit policy files directly on `master`. - Exception: direct updates to `master` are allowed only when explicitly instructed by the Owner. Default rule for any policy change (unless the exception above is explicitly invoked): - Any change to a policy must be made only: 1) by the Owner manually, OR 2) via a dedicated branch + Pull Request explicitly requested by the Owner. - Agents must never push policy changes to `master` (no direct commits, no force-push) unless the Owner explicitly invokes the exception above. - If the Owner requests a policy change via PR, the agent must: - propose the diff, - create a PR, - and wait for the Owner to review/merge. - If in doubt, do not modify. Ask. After — the same rule in MAIN_RULES.md:\n## Hard Rules 1. ALL code changes → branch + worktree. No exceptions. 2. Never push master/main. 3. Never merge PRs. Three lines. The same protection. Zero ambiguity.\nThe telegraphic style: less prose, more instruction The key to the reduction was not removing rules, but changing the format. We moved from explanatory prose to telegraphic style: short sentences, no unnecessary articles, no long justifications.\nA policy is not a document for humans. It is an instruction for a machine. It does not need to convince; it needs to be clear and brief.\nThis is counterintuitive. When you write policies for an AI agent, the natural instinct is to be thorough — explain the why, give examples, cover every edge case. But the more text you add, the more you dilute important instructions in noise.\nThere is a variant of this that is becoming trendy, called the \u0026ldquo;caveman\u0026rdquo; style — yes, seriously — which apparently also works very well, with a tremendous token reduction. I have not had the time or the inclination to try it yet, but I am sure I will.\nLess text, more compliance.\nLong sessions: the other enemy Optimized policies solve half the problem. The other half is long sessions.\nIn an intense work session, the agent can read dozens of files, run commands, receive long outputs, and accumulate hundreds of message exchanges. All of that stacks up in the context window. And when it approaches the limit, the system automatically compresses the oldest messages.\nThat compression is necessary, but it comes at a cost: the agent loses nuance. Decisions you made together at the beginning of the session, agreements on how to approach a problem, task-specific context — all of that fades.\nThe symptoms:\nRepeats questions you already answered. Re-reads files it already read. Contradicts decisions made earlier in the same session. Loses its tone — becomes more generic, less precise. The solution is not technical — it is operational: learn to end sessions at the right time. A fresh session with clean context performs better than a long session where the agent drags compressed tokens from an hour ago. Ask it to write a summary with the essentials needed to continue the unfinished work in a future session, and to leave it somewhere it can retrieve it.\nLessons learned After weeks of optimizing my agent\u0026rsquo;s context window, these are the lessons I take with me:\n1. The context window is a budget, not unlimited space. Every token you spend on policies is one less token for code, tools, and conversation. Manage that budget with the same discipline that maaaany years ago was used to manage RAM when programming.\n2. If the agent does not follow a rule, the problem might not be the agent. Before blaming the model, check how much text you are injecting. Sometimes the solution is not repeating the instruction — it is removing everything else.\n3. Write policies like code, not like documentation. Concise, unambiguous, no unnecessary prose. If a rule needs a paragraph of explanation, it is probably not a good rule.\n4. Sessions have a shelf life. Do not try to do everything in one session. Cut when you notice degradation. A new session with clear instructions is more productive than an old session with compressed context.\n5. Measure before you optimize. Ask the agent itself to measure the token cost of all injected files and list the biggest consumers. It can do this on its own in seconds.\nThe context window as a design constraint Working with an AI agent is not just about writing good prompts. It is about designing a system where the right information reaches the model at the right time, without saturating the available space.\nThe context window is not a technical detail you can ignore. It is the fundamental constraint that determines whether your agent is a reliable collaborator or a machine that generates generic responses.\nThe sooner you optimize it, the sooner you will stop wondering why your agent \u0026ldquo;stopped working.\u0026rdquo; It did not stop working. It lost relevant information that you are not giving it (without realizing).\n","permalink":"https://jupaygon.dev/en/posts/2026-04-14/context-window/","summary":"\u003cp\u003eEverything starts great.\u003c/p\u003e\n\u003cp\u003eYou give your AI agent instructions, it follows them to the letter. You define policies, it complies. You pass it work protocols, it executes them. It is fast, precise, and never complains.\u003c/p\u003e\n\u003cp\u003eUntil one day it stops.\u003c/p\u003e\n\u003cp\u003eNo error. No warning. It simply ignores rules it used to follow. It makes stupid mistakes. It repeats questions you already answered. What changed?\u003c/p\u003e\n\u003cp\u003eThe answer is the \u003cstrong\u003econtext window\u003c/strong\u003e — we get into the work and stop paying enough attention to it.\u003c/p\u003e","title":"Context window: when your AI agent starts forgetting"},{"content":"There is a lot of fear around AI right now.\nEvery week a new headline says developers are finished. That AI writes code better and faster than any human. That junior developers will not be hired anymore. That the entire profession has an expiration date.\nI understand the anxiety. If you have spent years learning your craft, seeing a machine generate in seconds what took you hours feels threatening. But I have been working with an AI agent as a daily collaborator for months now — and what I see is the opposite of replacement. I see amplification.\nThe fear is real. But the premise behind it is wrong.\nWhat people are actually afraid of Let us be specific. When developers say they fear AI, they usually mean one of these:\n\u0026ldquo;My job will disappear.\u0026rdquo; Companies will replace teams with AI tools and keep one person supervising the output. \u0026ldquo;Juniors are no longer needed.\u0026rdquo; If AI can write basic code, there is no entry point for new developers. \u0026ldquo;Understanding code does not matter anymore.\u0026rdquo; If you can generate it, why learn it? These fears share a common assumption: that programming is about typing code. That the value of a developer lives in the act of writing syntax.\nThat assumption is wrong — and it always has been.\nProgramming has never been about typing code Think about what programming actually looked like over time.\nIn the beginning, people programmed in binary. Literal ones and zeros, entered by hand. Then came assembly language — still low level, but at least readable by humans. Then high-level languages like C, which abstracted away the machine. Then object-oriented programming, which abstracted away the structure. Then frameworks — Rails, Django, Symfony, Spring — which abstracted away entire patterns and infrastructure.\nAt every single step, people said the same thing: \u0026ldquo;If the machine does this for us, what are we needed for?\u0026rdquo;\nAnd at every single step, the answer was the same: more. More developers were needed, not fewer. Because each abstraction layer made it possible to build things that were previously impossible — which created more demand, more complexity, and more need for people who understand what they are building.\nA developer using a modern framework is orders of magnitude more productive than someone writing assembly. But the assembly programmer did not become obsolete because frameworks exist. The role evolved. The abstraction level moved up. The thinking stayed.\nAI is the next abstraction layer.\nIt does not replace programming. It changes what programming looks like. Instead of writing every line by hand, you describe intent, review output, make architectural decisions, and validate results. The skill is no longer just \u0026ldquo;can you write a for loop.\u0026rdquo; It is \u0026ldquo;do you know what needs to be built and why.\u0026rdquo;\nWhat AI actually does (and what it does not) I have been working with an AI agent integrated into my daily development workflow. Not as a toy. Not for demos. For real production work.\nHere is what it does well:\nGenerates boilerplate fast. CRUD operations, migrations, repetitive patterns — things that are mechanical and well-defined. Explores codebases efficiently. It can read, search, and cross-reference thousands of lines faster than I can. Applies known patterns. If the solution follows a documented or common pattern, it executes it reliably. Iterates on feedback. You review, correct, and it adjusts. The loop is fast. Here is what it does not do:\nIt does not understand your business. It does not know why your client needs this feature, what the trade-offs are, or what the users actually care about. It does not make architectural decisions. It can suggest patterns, but deciding how systems should interact, what to decouple, when to optimize — that requires judgment and context that no model has. It does not negotiate. Requirements, deadlines, priorities — these are human conversations. It does not take responsibility. When something breaks in production, someone has to diagnose, decide, and fix. That someone understands the system, not just the code. The AI is a powerful tool. But a tool without a skilled operator is just noise.\nThe real comparison is wrong Most fear comes from the wrong comparison.\nPeople compare \u0026ldquo;a developer today\u0026rdquo; versus \u0026ldquo;AI alone.\u0026rdquo; And in that comparison, AI looks threatening — it writes code faster than any human.\nBut that is not the real comparison. The real one is:\nA developer without AI versus a developer with AI.\nAnd in that comparison, the developer with AI wins every time. Not because the AI replaces their skill, but because it multiplies it.\nIf you know what to build, AI helps you build it faster. If you understand architecture, AI implements your designs in minutes. If you can evaluate code quality, AI gives you more code to evaluate — and the bottleneck shifts from production to judgment.\nThe developer who embraces AI becomes dramatically more productive. The one who ignores it stays where they are.\nBut be careful: AI multiplies — and you can multiply by zero Here is the critical nuance that most \u0026ldquo;AI will save us all\u0026rdquo; narratives miss.\nAI is a multiplier. But multiplying by zero is still zero.\nIf you hand everything to the AI — the design, the decisions, the validation, the understanding — you become zero. You are not a developer anymore. You are a prompt relay. And a prompt relay is, in fact, replaceable.\nThe developers who should worry are not the ones competing against AI. They are the ones who stop thinking because AI thinks for them. The ones who accept generated code without understanding it. The ones who lose the ability to evaluate whether the output is correct, secure, maintainable, or aligned with the business need.\nAI amplifies what you bring to the table. If you bring deep understanding, critical thinking, and domain expertise — it makes you extraordinary. If you bring nothing and just press \u0026ldquo;accept\u0026rdquo; — it makes you redundant.\nThe skill floor has not dropped. It has shifted. You need fewer people who can write syntax. You need more people who can think.\nWhat skills gain value now If AI handles more of the \u0026ldquo;writing code\u0026rdquo; part, what becomes more valuable?\nSystems thinking. Understanding how components interact, where bottlenecks will appear, what happens at scale. Domain knowledge. Knowing the business, the users, the regulations, the context that no model has access to. Critical evaluation. Being able to look at generated code and say \u0026ldquo;this will fail under load\u0026rdquo; or \u0026ldquo;this violates our security policy\u0026rdquo; — quickly and confidently. Communication. Translating between business needs and technical solutions. This was always important; now it is essential. Architecture and design. Deciding what to build and how to structure it. AI can implement a design. It cannot decide the right design. None of these are new skills. They are the skills that senior developers have always had. The difference is that now they are not optional anymore — even at earlier career stages.\nThe opportunity is right now This is not a future scenario. It is happening today.\nDevelopers who learn to work with AI effectively have an immediate advantage. They ship faster, handle more complexity, and free themselves from the tedious parts of the job to focus on the interesting parts.\nThe opportunity is not \u0026ldquo;learn AI\u0026rdquo; in the abstract. It is specific:\nIntegrate AI into your actual workflow. Not a chatbot on the side. A real collaborator in your development environment. Develop your ability to evaluate and direct. The better you are at reviewing output, the more value you extract from the tool. Invest in the things AI cannot do. Architecture, business understanding, communication, leadership. These are what set you apart. Stay curious. The tools are evolving fast. What is limited today will not be limited next year. Adapt continuously. And if you are not a developer but you are building with AI This one is for you — the designer, the entrepreneur, the product person, the curious mind who discovered that AI can write code and started building things you never thought you could.\nFirst: keep going. Seriously. The fact that you are building, experimenting, and shipping is fantastic. AI has lowered the entry barrier to software creation in a way that was unthinkable five years ago. That is genuinely exciting.\nNow, you have two paths — and both are valid.\nOption 1: Learn the fundamentals The more you understand what is happening underneath, the better you can direct the AI, evaluate its output, and build things that actually work at scale. You do not need to become a full-time software engineer, but the basics make a real difference:\nWhat is SOLID and why it makes code maintainable. What are design patterns — recurring solutions to common problems that developers have refined over decades. What is object-oriented programming — how to model real-world concepts in code. What is layered architecture (hexagonal architecture) — how to separate concerns so your application does not become an unmaintainable mess. What are code tests — how to verify that your software actually does what you think it does. What is DDD (Domain-Driven Design) — how to model software around the actual business problem, not the technology. What is TDD (Test-Driven Development) — writing tests before code, so the design emerges from real needs. And here is the best part: you already have the perfect learning tool. The same AI that writes code for you can explain these concepts, generate summaries, walk you through examples, and answer your questions at any level of depth. Ask it to explain SOLID with examples from your own project. Ask it to refactor your code following a design pattern and explain why. Use it as a teacher, not just as a code generator.\nOption 2: Build prototypes and collaborate with developers If learning to code is not your focus — that is perfectly fine. But what you can build with AI is still enormously valuable: prototypes, ideas, functional MVPs. And then hand them off to a development team.\nThink about it. Before, to communicate an idea to a developer you needed mockups, wireframes, specification documents. Now you can hand them something that actually works. A living prototype. You barely need to explain what you want anymore — they can simply see it.\nYour MVP becomes what product mockups and design comps used to be, but far more real, far more alive. And the development team can use it as the foundation to build something solid — with the architecture, tests, and quality that a product needs to scale.\nYou do not need to do it all yourself. You need to know enough to start — and have someone who knows enough to finish it right.\nAnd if you are a junior developer: this is for you too There is a narrative that says juniors are no longer needed. That if AI writes basic code, there is no point in hiring someone who is still learning.\nThat narrative is wrong.\nAI does not eliminate the need for juniors — it changes what a junior needs to learn first. Before, the first thing was mastering syntax. Now syntax is the least important part. The first thing is understanding why something is built a certain way, not how to write it.\nIf you are a junior, you have an advantage that no previous generation had: you can learn with an infinitely patient tutor that knows about everything. Ask without fear. Ask it to explain every line. Ask it to show you alternatives and tell you which one is better and why. Do not just copy what it generates — understand it.\nThe juniors who grow up with AI as a learning tool will become the most complete seniors the industry has ever seen. But only if they use AI to learn, not to avoid learning.\nFear is natural. Stagnation is the real risk Every generation of developers faced a moment where the tools changed dramatically. Every time, the ones who adapted thrived. The ones who refused to learn the new tools got left behind — not by the tools, but by the people who used them.\nAI is not the end of software development. It is the beginning of a new layer. Like every layer before it, it will make us capable of building things we cannot even imagine today.\nThe risk is not AI. The risk is standing still.\nThe question is not whether AI will change your job. It will. The question is whether you will be the one using it — or whether you will stand still watching the opportunities pass you by.\n","permalink":"https://jupaygon.dev/en/posts/2026-04-07/ai-fear-vs-opportunity/","summary":"\u003cp\u003eThere is a lot of fear around AI right now.\u003c/p\u003e\n\u003cp\u003eEvery week a new headline says developers are finished. That AI writes code better and faster than any human. That junior developers will not be hired anymore. That the entire profession has an expiration date.\u003c/p\u003e\n\u003cp\u003eI understand the anxiety. If you have spent years learning your craft, seeing a machine generate in seconds what took you hours feels threatening. But I have been working with an AI agent as a daily collaborator for months now — and what I see is the opposite of replacement. I see amplification.\u003c/p\u003e","title":"AI will not replace you. But it will multiply you — careful with being zero"},{"content":"On March 31, 2026, the Claude Code source code was briefly exposed. A Korean developer named Sigrid Jin made a clean-room port to Python and Rust before Anthropic took it down. The claw-code repository does not contain the original code, but it does include snapshots, subsystem metadata, and a partial runtime port that reveals how Claude Code works under the hood.\nUnderstanding how your tool works lets you use it better, so it was worth digging into it thoroughly. For obvious reasons it would take me several years to review every line of code, but that is what Jarvis is for \u0026mdash; he got to work and abstracted away the first 50K hours of effort for me ;-)\nSome of the things we found were already more or less obvious, depending on how far each person has read or researched, so I hope the more advanced readers will bear with me. For the rest, I think it is worth including them in this summary.\n1. CLAUDE.md is recursive: use a hierarchy of instructions This is what changed my workflow the most.\nClaude Code does not look for a single CLAUDE.md at the root of your project. It searches for instruction files across the entire ancestor directory chain, from your current working directory all the way up to the filesystem root. In each directory it looks for:\nCLAUDE.md CLAUDE.local.md .claude/CLAUDE.md .claude/instructions.md All of them are loaded and concatenated (with automatic deduplication if two files have the same content). The total budget is 12,000 characters, with a maximum of 4,000 per file.\nHow to use it: put global rules in ~/CLAUDE.md (code style, commit conventions, security rules), team or company rules in ~/Work/CLAUDE.md, and project-specific rules in each repo. They all apply, in cascade.\nOne detail: CLAUDE.local.md is meant for instructions you do not want to commit. Add it to .gitignore and use it to inject personal context: local paths, environment variables, individual preferences.\nThat said: not everything has to live in the CLAUDE.md. In my case, the CLAUDE.md is just a few lines \u0026mdash; it points to a dedicated repository with policies, protocols and skills organized in folders. If your CLAUDE.md starts growing too much, consider splitting it up. But that deserves its own post.\n2. Hooks are real security guards, not suggestions The Claude Code system prompt tells it things like \u0026ldquo;do not run destructive commands\u0026rdquo;. But that is an instruction to the model \u0026mdash; a suggestion it can ignore.\nHooks are different. They are real shell commands that run before and after each tool use. If a hook returns exit code 2, the tool is blocked. There is no way around it.\nThe system passes all the necessary information to the hook as environment variables:\nHOOK_EVENT \u0026mdash; PreToolUse or PostToolUse HOOK_TOOL_NAME \u0026mdash; tool name (e.g. Bash) HOOK_TOOL_INPUT \u0026mdash; the full JSON input HOOK_TOOL_OUTPUT \u0026mdash; the output (PostToolUse only) Practical example: a hook that blocks dangerous commands:\n#!/bin/bash # PreToolUse hook: block destructive commands if echo \u0026#34;$HOOK_TOOL_INPUT\u0026#34; | jq -r \u0026#39;.command // empty\u0026#39; | grep -qE \u0026#39;rm -rf /|DROP TABLE|format \u0026#39;; then echo \u0026#34;Dangerous command blocked by hook\u0026#34; exit 2 fi This is infinitely more reliable than trusting the model to \u0026ldquo;obey\u0026rdquo; prompt instructions.\n3. Auto-compaction has a configurable threshold When a session gets long, Claude Code automatically compacts older messages into a structured summary. The summary preserves: tools used, key files, pending work, and a conversation timeline. The most recent messages are kept intact.\nWhat is not obvious is that the threshold is adjustable. By default it triggers at ~95% of context capacity, but you can force more aggressive or more conservative compaction.\nTip: for long sessions where the agent \u0026ldquo;loses the thread\u0026rdquo;, compact manually with /compact. The summary it generates is surprisingly good \u0026mdash; it captures pending work, touched files, and what you were doing. It is like a checkpoint for your session.\n4. The system prompt is built dynamically The system prompt is not a fixed text. It is assembled piece by piece by a builder with modular sections:\nIntro \u0026mdash; defines the agent role Output Style \u0026mdash; formatting instructions (optional, only if you configure one) System \u0026mdash; base rules (permissions, hooks, compression) Doing Tasks \u0026mdash; behavior guide (\u0026ldquo;do not add speculative abstractions\u0026rdquo;) Actions \u0026mdash; precautionary principle for destructive operations __SYSTEM_PROMPT_DYNAMIC_BOUNDARY__ \u0026mdash; marker separating the static part from the contextual one Environment \u0026mdash; model, directory, date, OS Project Context \u0026mdash; git status + git diff at session start CLAUDE.md \u0026mdash; your instructions, recursively Runtime Config \u0026mdash; loaded settings That DYNAMIC_BOUNDARY is key: everything before it is cacheable by the Anthropic API. Everything after it changes with each session.\nPractical implication: keeping your CLAUDE.md stable (without frequent changes) maximizes prompt cache hits, which reduces latency and cost. If you change your CLAUDE.md constantly, you invalidate the cache.\n5. Each tool has a required permission level The permission system is not a simple on/off. Each tool has an assigned required_permission:\nTool Required permission read_file, glob_search, grep_search ReadOnly write_file, edit_file, TodoWrite WorkspaceWrite bash, Agent, REPL DangerFullAccess If your permission mode is lower than required, Claude Code asks before executing. If it is equal or higher, it executes directly.\nTip: for read-only tasks (audits, code exploration, documentation), use ReadOnly mode. The agent will not be able to modify anything even if it wants to. For CI/CD automation without intervention, use Allow.\n6. Subagents support model override When Claude Code launches a subagent (with the Agent tool), it accepts a model parameter that allows using a different model than the main one.\nThis is already built into the normal flow: when you ask for something that requires search, Claude Code can launch a Haiku agent (faster and cheaper) for the search, and reserve the main model for complex reasoning.\nHow to use it: if you are in an Opus session and need to search for something simple, the system already optimizes this automatically. But if you define your own agents (custom agents in .claude/agents/), you can specify which model to use for each one.\n7. Claude Code captures git status and diff at startup At the start of each session, the runtime captures a snapshot of git status --short --branch and git diff (both staged and unstaged). This is injected into the system prompt as context.\nImplication: Claude Code knows from the first message which files you have changed, what you have staged, and which branch you are on. You do not need to explain \u0026ldquo;I am on branch feature/X and I have modified these files\u0026rdquo; \u0026mdash; it already knows.\nTip: if you want Claude Code to start from a clean state, commit or stash before starting the session. If you want it to know exactly what you have touched, leave the changes uncommitted.\n8. \u0026ldquo;Deferred\u0026rdquo; tools are loaded on demand Not all tools are available from the start. Some are \u0026ldquo;deferred\u0026rdquo; \u0026mdash; they appear listed in \u0026lt;system-reminder\u0026gt; tags but have no schema until they are loaded with ToolSearch.\nThis is an optimization: loading all tools from the beginning would consume too much context. Instead, the model sees the available names and loads the full definition only when needed.\nTip: if you need a specific tool and Claude Code does not seem to find it, mention it by name. The system will use ToolSearch to load its schema and make it available.\n9. There is a bundled skills system beyond the visible ones The source reveals 20 skill modules, several not obvious from the interface:\n/loop \u0026mdash; runs a command or prompt cyclically with a configurable interval. Perfect for polling (e.g. watching an open PR every 5 minutes for new comments or status changes). /simplify \u0026mdash; reviews changed code looking for reuse, quality and efficiency opportunities. Auto-fixes what it finds. /schedule \u0026mdash; schedules remote agents with cron. The foundation for recurring automated tasks. skillify \u0026mdash; a meta-skill that converts a frequent usage pattern into a new reusable skill. These skills do not always show up in the basic help, but they are there. Try /loop 5m /status to monitor something, or /simplify after a big refactor.\n10. The sandbox is much more than limiting paths On Linux, Claude Code uses bubblewrap (which internally creates user, mount, IPC, PID and UTS namespaces with unshare). On macOS it uses Seatbelt. It can isolate the network completely.\nIt also automatically detects if it is running inside Docker, Podman or Kubernetes \u0026mdash; by checking /.dockerenv, /run/.containerenv, /proc/1/cgroup and environment variables like KUBERNETES_SERVICE_HOST.\nWhy it matters: if you work with sensitive data or in regulated environments, the Claude Code sandbox is not a toy. It is real kernel-level isolation. And if you are inside a container, it detects it and adjusts its behavior accordingly.\nBonus: the real scale of Claude Code The numbers from the original source put things in perspective:\n1,902 TypeScript files 207 slash commands 184 tool modules 33 independent subsystems 130 modules in the services directory alone This is not a CLI that calls an API. It is a full agent runtime, with a plugin system, multi-agent coordination, vim mode, voice mode, an animated companion (yes, there is a buddy with sprites), and subsystems that have not been publicly activated yet.\nConclusions Understanding how your tool works gives you an edge. The three things with the most practical impact:\nHierarchical CLAUDE.md \u0026mdash; use the file cascade to have global, team and project rules. It is the most effective way to control agent behavior. Hooks as real guards \u0026mdash; if you need real security (not suggestions), hooks with exit code 2 are the only barrier the model cannot bypass. CLAUDE.md stability \u0026mdash; keeping it stable maximizes prompt cache. Do not change it constantly. The Claude Code source confirms something that was already obvious: if you want to make a difference, where you have room to maneuver and plenty to improve in your productivity and results, is in the orchestration. How you configure the environment, how you structure the instructions, how you connect the tools. That is where the real advantage lies.\n","permalink":"https://jupaygon.dev/en/posts/2026-04-01/claude-code-internals/","summary":"\u003cp\u003eOn March 31, 2026, the Claude Code source code was briefly exposed. A Korean developer named \u003ca href=\"https://github.com/instructkr\" target=\"_blank\"\u003eSigrid Jin\u003c/a\u003e made a clean-room port to Python and Rust before Anthropic took it down. The \u003ca href=\"https://github.com/instructkr/claw-code\" target=\"_blank\"\u003eclaw-code\u003c/a\u003e repository does not contain the original code, but it does include snapshots, subsystem metadata, and a partial runtime port that reveals how Claude Code works under the hood.\u003c/p\u003e\n\u003cp\u003eUnderstanding how your tool works lets you use it better, so it was worth digging into it thoroughly. For obvious reasons it would take me several years to review every line of code, but that is what \u003ca href=\"https://github.com/jarvis-aidev\" target=\"_blank\"\u003eJarvis\u003c/a\u003e is for \u0026mdash; he got to work and abstracted away the first 50K hours of effort for me ;-)\u003c/p\u003e","title":"Inside Claude Code: 10 interesting things from analyzing its source code"},{"content":"Your AI agent has a problem it does not tell you about: it does not know what it does not know. When you ask it to work with a library or tool that has changed since its training cutoff, it generates code with full confidence\u0026hellip; using methods that no longer exist. It is not lying. It simply does not have the information.\nThis is the knowledge cutoff problem, and it affects every model. For stable, mature technologies, the impact is small. But for libraries, bundles, and tools that evolve fast, it is a real problem that produces silent errors: code that looks correct but fails at runtime.\nThe fix is not better prompting or bigger models. It is giving the agent up-to-date, verified information in a format it can consume. That is exactly what an AI Skill does.\nWhat is an AI Skill A skill is a structured Markdown document containing verified knowledge about a specific technology. It is not a tutorial. It is not generic documentation. It is a reference optimized for AI agents: exact API signatures, version-to-version changes, correct patterns, minimal and copy-paste-ready examples.\nThink of it as the difference between handing a new developer a 500-page book and giving them the verified cheat sheet for the exact version you are running. The agent does not need to understand the entire history of a library \u0026mdash; it needs to know which methods exist now, which ones disappeared, and how to use them.\nA typical skill includes:\nBreaking changes table between versions \u0026mdash; the first thing the agent checks API signatures verified against source code, not against documentation that may be outdated Code examples that are minimal and functional, not tutorials Notes on common pitfalls \u0026mdash; things that look correct but are not How an agent consumes it Integration is simple because a skill is just an .md file. No installation, no dependencies, no build step.\nIn Claude Code, you place it in .claude/skills/ inside your project, or reference it from your CLAUDE.md:\n# CLAUDE.md When working with EasyAdmin, read the skill at /path/to/skills/easyadmin5/SKILL.md before writing any code. In Cursor, it goes in .cursor/rules/. In other agents, wherever you place project context.\nWhen the agent is about to generate code with that technology, it reads the skill first. It is like checking the docs before writing \u0026mdash; but automated and with information we know is correct.\nWhy official documentation is not enough Official documentation is designed for humans. It has narrative context, progressive explanations, didactic examples. That is fine for learning, but an AI agent needs something different:\nExact signatures, not explanatory paragraphs Version-to-version changes explicit and in tables, not buried in an UPGRADE.md Data verified against the actual source code, not against documentation that sometimes drifts from the code Machine-friendly format: tables, lists, code blocks \u0026mdash; not prose On top of that, official documentation covers everything. A skill covers what the agent needs to write correct code: the public API, breaking changes, patterns that work.\nHow to create a reliable skill This is where it gets interesting. Creating a skill is not copying documentation and reformatting it. It is an iterative verification process involving humans and multiple AI agents, each with a different role.\nThe process we followed:\n1. Initial extraction \u0026mdash; One agent researches the official documentation, the package source code, the UPGRADE.md, and produces a first draft with the complete API: methods, signatures, parameters, breaking changes.\n2. First review \u0026mdash; A different agent, not the one that wrote the draft, verifies every claim against the real source code. Not against the documentation \u0026mdash; against the code. Does this method exist? Is this signature correct? Is this parameter object or mixed?\n3. Correction and second review \u0026mdash; Errors are fixed and a third agent reviews again. The principle is the same as code review: fresh eyes catch things the author misses.\n4. Human verification \u0026mdash; The human reviews the result, applies judgment about what to include and what to leave out, validates corrections, and sometimes discovers that one reviewer was wrong and another was right.\n5. Iteration \u0026mdash; Repeat until the error rate converges to zero.\nWhy multiple agents instead of just one? For the same reason you do not review your own code: confirmation bias. An agent that wrote something will tend to validate it. A different agent questions it.\nReal example: EasyAdmin 5 All of this sounds abstract, so let me walk through a concrete case.\nEasyAdmin is one of Symfony\u0026rsquo;s most popular bundles. Version 5 was released as the new stable version, removing everything deprecated in the 4.x series. Functionally it is similar, but the API changed in critical places:\nlinkToCrud() → linkTo() displayAsLink() → renderAsLink() addPanel() → addFieldset() {{ ea.property }} in Twig → {{ ea().property }} Pretty URLs now mandatory PHP 8.2+ and Symfony 6.4+ required These look like minor changes, but they produce runtime errors that are not obvious. And the worst part: your agent is convinced its code is correct, because it was\u0026hellip; in version 4.\nThe trigger was practical: I was migrating one of my projects from EA4 to EA5 with Jarvis, and the results were frustrating. It used version 4 methods with full confidence, assumed EA4 features still worked in EA5, and generated code that failed at runtime over and over. It was not a capability problem \u0026mdash; it was an information problem.\nJavier Eguiluz (EasyAdmin\u0026rsquo;s creator) has written about how to use Claude Code with Symfony, but there is no official EasyAdmin 5 skill for AI agents \u0026mdash; a verified, structured reference specific to EA5 that agents can consume directly.\nSo I decided to build one and share it with the Symfony community, hoping that more developers find it as useful as I do and can take advantage of it.\nWhat the reviewers found In the first review, an agent verified the draft against EasyAdmin 5\u0026rsquo;s source code and found 8 factual errors.\nIn the second review, another agent found 5 more errors: an attribute with incorrect parameters (routePath instead of path), a misleading number format, incomplete ImageField tokens.\nIn the third review: 2 more errors. And in the fourth: 1 error and 1 imprecision.\nEach round converged: 8 → 5 → 2 → 1. Until we reached a document with over 900 verified lines where every signature, every parameter, and every example is checked against the bundle\u0026rsquo;s actual source code.\nThe result Before the skill, asking an agent to create a menu in EasyAdmin 5 produced code with linkToCrud() (EA4). After the skill, it generates linkTo() (EA5) on the first try. Same with fields, actions, filters, events, Twig templates.\nThe skill covers the complete public API: dashboard, menus, CRUD controllers, 30 field types with detailed documentation, actions and action groups, batch actions, filters, multi-level security, lifecycle events, pretty URLs, design customization, and URL generation.\nSkills as an investment Creating a skill takes time. But it is an investment that pays off every time you \u0026mdash; or any other developer \u0026mdash; asks an agent to work with that technology.\nIt is open source. Verified. Maintained. A Markdown file that anyone can copy into their project and start using.\nIf you work with EasyAdmin and an AI agent, try it. And if you work with another technology that changes between versions, consider creating your own skill. The process I have described works, and the result is the difference between an agent that hallucinates and one that writes correct code on the first try.\nThe EasyAdmin 5 skill is available at jupaygon/ai-skills. If you find it useful, a star on the repo helps other developers discover it.\n","permalink":"https://jupaygon.dev/en/posts/2026-03-29/ai-skills/","summary":"\u003cp\u003eYour AI agent has a problem it does not tell you about: it does not know what it does not know. When you ask it to work with a library or tool that has changed since its training cutoff, it generates code with full confidence\u0026hellip; using methods that no longer exist. It is not lying. It simply does not have the information.\u003c/p\u003e\n\u003cp\u003eThis is the \u003cstrong\u003eknowledge cutoff\u003c/strong\u003e problem, and it affects every model. For stable, mature technologies, the impact is small. But for libraries, bundles, and tools that evolve fast, it is a real problem that produces silent errors: code that looks correct but fails at runtime.\u003c/p\u003e","title":"AI Skills: how to give your AI agent verified knowledge it does not have"},{"content":"There is a lot of noise about AI and programming. Copilots, autocomplete, code generators\u0026hellip; Most tools stop at suggesting the next line. But you can do something different, something more: work with an AI agent that operates as another developer on the team.\nMy digital teammate is called Jarvis. It has its own GitHub account, its own Jira access, and a workflow identical to any human developer: branch, implementation, commit, pull request, code review. I have been running this system in production for months, and this is what I have learned.\nWhat AI-Augmented Development is (and what it is not) It is not asking an AI to write you a function. It is not smart autocomplete. It is not copying and pasting from ChatGPT.\nAI-Augmented Development is integrating an AI agent into your real workflow, with the same tools, the same constraints, and the same process as a human developer:\nIsolated branch — each task on its own branch, never on master. Pull request — all work goes through PR. I review, comment, request changes. Limited permissions — the agent cannot merge or push to master. Branch protection rules prevent it. Traceability — every commit is tied to its author. The git log shows who did what. The difference from a copilot is that Jarvis understands the full project context, not just the file you have open. It brings speed and shorter learning curves, always as a complement that makes you a better programmer, not as a replacement.\nAn important nuance: Jarvis is not simply opening a Claude Code or Codex session and asking it to do things. It has taken months of iteration. Policies that define what it can and cannot do, procedures for each type of task, connected tools (GitHub, Jira, MCP servers), security rules, code conventions. All of that is structured prompt engineering, and it is what turns a chat session into something viable for real work.\nThe upside: ask your own agent to help you refine it. When something goes wrong, ask it: how can we fix this? How can we prevent you from making this mistake again? Review your policies and tell me where and how you propose the change. Or for a new procedure: draft a procedure for next time, and how we will enforce that you follow it to the letter on similar tasks. The agent improves its own configuration. It is a self-reinforcing continuous improvement cycle.\nProductivity: what really changes The productivity boost is real, but it does not come from where people think.\nIt is not that the code gets written faster. It is that I spend more time on what matters: designing the architecture, thinking through edge cases, reviewing with judgment. The mechanical tasks \u0026mdash; boilerplate, CRUDs, migrations, repetitive unit tests \u0026mdash; Jarvis handles those. And it does them well, because it has the project context.\nTasks that used to take hours now take minutes. Those that took days now take hours. And I am not talking about sloppy code that needs to be redone. The code passes review, passes tests, and follows project standards.\nA concrete example: the symfony-dashboard-skeleton \u0026mdash; a full dashboard with EasyAdmin 5, multi-tenant, 4 themes, role hierarchy, i18n \u0026mdash; we built it in hours. Not weeks. Hours. With tests, documentation, and a complete README.\nDocumentation: the change I least expected If there is one thing that has improved dramatically and I did not expect, it is documentation.\nMost developers hate documenting. Others enjoy it but never have the time. I was in the second group. Now, documentation is generated as a natural part of the workflow. Jarvis documents what it implements: READMEs, PR comments, Jira ticket descriptions. And I review and adjust the tone.\nThe result is that my projects have better documentation than ever. Not as an afterthought you do at the end when you remember, but as part of the process. Every PR has its description, every ticket its summary, every repo its updated README.\nFor example, I have started creating Project Books: living HTML documents with charts, colors, and links that reflect the full life of a project \u0026mdash; from the initial brainstorm to production and beyond. They include the roadmap, architectural decisions, links to Jira tickets, current status of each piece. It is like a project dashboard, but narrative. And keeping them up to date is no longer extra effort \u0026mdash; it is part of the flow.\nStack: the range opens up This is perhaps the most unexpected change.\nYou can spend your entire career in a specific language and frameworks, in your comfort zone. But programming is programming \u0026mdash; good practices, SOLID, separation of concerns, layered architecture\u0026hellip; are universal and independent of the language.\nWith this workflow, I bring the experience in those principles and Jarvis masters the syntactic details of each language. Need a Python script? A Go component? Infrastructure configuration? I can work confidently with stacks that would have previously required weeks of learning curve.\nIt is not that I have become a Python expert overnight. It is that I can apply over two decades of engineering judgment in any language, because syntax is no longer a bottleneck.\nSecurity: this is not a toy When I tell other developers that I have an AI agent with its own GitHub account, some raise an eyebrow. Sounds risky. But it is exactly the opposite: it is the safest approach.\nSeparate credentials — Jarvis does not have my tokens or passwords. It has its own, with limited permissions. Branch protection — It cannot push to master or merge PRs. Branch protection rules prevent it. Scoped permissions — In Jira it can create and move tickets, but cannot delete or modify configurations. Full traceability — Every action is logged under its identity. If something goes wrong, I know exactly what it did and when. The alternative \u0026mdash; giving it your personal token with all your permissions \u0026mdash; is far more dangerous. And it is what most people do without thinking.\nWhat I have learned After months with this system, these are my takeaways:\nAI does not replace the developer, it amplifies them. Engineering judgment remains human. AI executes, the human designs and reviews. The workflow matters more than the model. It does not matter whether you use Claude, GPT, or Gemini. What makes the difference is how you integrate AI into your process. Documentation solves itself when it is part of the flow, not an afterthought. Stack is no longer a barrier. Engineering principles are universal. Syntax is a detail. Security is not optional. Separate identity, limited permissions, branch protection. No shortcuts. If you are a developer and still think of AI as \u0026ldquo;the autocomplete that sometimes gets it right\u0026rdquo;, I encourage you to explore this other way of working. It is not perfect, but it has radically changed how I build software.\nThis blog \u0026mdash; including this post \u0026mdash; was built following exactly the workflow I describe. You can see it in the repository.\n","permalink":"https://jupaygon.dev/en/posts/2026-03-26/ai-augmented-development/","summary":"\u003cp\u003eThere is a lot of noise about AI and programming. Copilots, autocomplete, code generators\u0026hellip; Most tools stop at suggesting the next line. But you can do something different, something more: work with an AI agent that operates as \u003cstrong\u003eanother developer on the team\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eMy digital teammate is called Jarvis. It has its own GitHub account, its own Jira access, and a workflow identical to any human developer: branch, implementation, commit, pull request, code review. I have been running this system in production for months, and this is what I have learned.\u003c/p\u003e","title":"AI-Augmented Development: my real experience working with an AI agent"},{"content":"Who I am I\u0026rsquo;ve been building software since 2003. I\u0026rsquo;ve been a CTO and Co-Founder for most of my career, leading technical teams and architecting high-concurrency, performance-critical systems.\nCurrently I\u0026rsquo;m Co-Founder \u0026amp; CTO at Zeronet IT, where we work on improving the energy efficiency of IT networks, and where we\u0026rsquo;ve integrated AI as a core part of both our workflow and the final product.\nWhat I do ZeroNet helps reduce the energy consumption of IT networks. It\u0026rsquo;s a real problem: network infrastructures consume far more than they need to, and most don\u0026rsquo;t even realize it. We combine intelligent monitoring to improve network efficiency. We cover data centers, end devices, shadow AI agent detection, and cyber threat alerting (based especially on energy consumption patterns). More info: zeronetit.com.\nI\u0026rsquo;ve incorporated AI as a central part of my workflow. Not as code autocomplete, but as AI-Augmented Development \u0026mdash; AI agents that operate as real collaborators: creating branches, writing code, opening pull requests, and responding to code review.\nI\u0026rsquo;ve decided to share this approach and contribute to the community through this blog, and with open source tools that I keep adding to my GitHub account, some directly related to AI, others simply built with its help. I don\u0026rsquo;t have grand aspirations \u0026mdash; if just one developer benefits from these experiences, I\u0026rsquo;ll be more than satisfied :-).\nJarvis: my AI collaborator Jarvis is an AI developer agent with its own GitHub identity. It creates branches, writes code, opens pull requests, manages Jira tickets, and responds to code review \u0026mdash; all under human supervision.\nWhy a separate identity instead of just using my account? Security and real separation of responsibilities. Jarvis has its own credentials on GitHub and Jira, with limited permissions: it can create branches, make commits, and open pull requests, but it cannot commit to master or merge PRs \u0026mdash; branch protection rules prevent it. It has no access to my passwords or tokens. If something goes wrong, the damage is contained. It might look like a toy, but it\u0026rsquo;s an engineering and security decision.\nJarvis is not fine-tuning or a custom model. It\u0026rsquo;s prompt engineering with structured context: policies, skills, protocols, and tools (MCP servers) that define its behavior. The result is an agent that works like another developer on the team \u0026mdash; autonomous inside its branch, but always waiting for peer review before merging.\nAll my work shows the collaboration (visible in public repositories): Jarvis implements, I design and review. The git log tells the real story.\nThe result The productivity impact is enormous. Tasks that used to take hours now take minutes, and those that took days now take hours, while maintaining the same quality and rigor. It\u0026rsquo;s not about writing code faster, but about having more time for what truly matters: designing well, thinking through the architecture, and reviewing with good judgment.\nOn top of that, this workflow has broadened my reach across stacks. Programming is programming \u0026mdash; good practices, SOLID, separation of concerns, layered architecture\u0026hellip; are universal and independent of the chosen programming language. I bring my experience in those principles, and Jarvis masters the syntactic details of each language. The result is that I can work confidently with stacks that would have previously required weeks or months of learning curve.\nContact GitHub: jupaygon LinkedIn: jupaygon X: jupaygon Digital card ","permalink":"https://jupaygon.dev/en/about/","summary":"About Juanjo Payá","title":"About"},{"content":"","permalink":"https://jupaygon.dev/en/card/","summary":"Juanjo Payá — Digital Card","title":"Card"}]