<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Code-Review on Lessons learned</title><link>https://eghuro.cz/tags/code-review/</link><description>Recent content in Code-Review on Lessons learned</description><generator>Hugo</generator><language>en-us</language><copyright>© Alexandr Mansurov · Licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)</copyright><lastBuildDate>Sun, 25 Oct 2026 21:50:00 +0100</lastBuildDate><atom:link href="https://eghuro.cz/tags/code-review/index.xml" rel="self" type="application/rss+xml"/><item><title>Adapt security skills to your project's risks: from Go to OWASP LLM and iOS</title><link>https://eghuro.cz/posts/adapt-security-skills-to-project-risks/</link><pubDate>Sun, 25 Oct 2026 21:50:00 +0100</pubDate><guid>https://eghuro.cz/posts/adapt-security-skills-to-project-risks/</guid><description>&lt;p&gt;A security skill written for Go covers different questions than one written for an LLM application or an iOS app. I started with &lt;code&gt;golang-security&lt;/code&gt; from &lt;a&#10; href="https://github.com/samber/cc-skills-golang"&#10; &#10; target="_blank" rel="noopener"&#10; &#10; &#10;&gt;samber/cc-skills-golang&lt;/a&gt;&#10;, asked Claude to adapt it using the OWASP Top 10 for LLM Applications, and later carried that approach into production iOS development.&lt;/p&gt;&#10;&lt;p&gt;The most useful part of the adaptation was aligning the review questions with the project&amp;rsquo;s risks and the company&amp;rsquo;s values.&lt;/p&gt;&#10;&#10;&lt;h2 id="start-with-a-review-method"&gt;Start with a review method &lt;/h2&gt;&#10;&lt;p&gt;I was working in Go, a language I was unfamiliar with. I used my security skills alongside other Go skills to keep the implementation in check.&lt;/p&gt;&#10;&lt;p&gt;The security skill provided a repeatable method: separate modes for coding, reviewing changes, and auditing a codebase; questions about trust boundaries and attacker-controlled input; and instructions to trace data through surrounding code before reporting a finding. Its references and checklist gave the agent somewhere to look beyond the main instructions.&lt;/p&gt;&#10;&#10;&lt;h2 id="adapt-owasp-categories-to-the-application"&gt;Adapt OWASP categories to the application &lt;/h2&gt;&#10;&lt;p&gt;I asked Claude to create a new skill using the &lt;a&#10; href="https://genai.owasp.org/llm-top-10"&#10; &#10; target="_blank" rel="noopener"&#10; &#10; &#10;&gt;OWASP Top 10 for LLM Applications (2025)&lt;/a&gt;&#10;, adapted to the project&amp;rsquo;s LLM and Model Context Protocol (MCP) surfaces.&lt;/p&gt;&#10;&lt;p&gt;The resulting &lt;code&gt;llm-security&lt;/code&gt; skill kept the review modes and evidence requirements. It mapped the categories to tools, resources, prompts, and content returned to the model. The Go checklist stayed active for the code underneath.&lt;/p&gt;&#10;&lt;p&gt;For &lt;a&#10; href="https://genai.owasp.org/llmrisk/llm01-prompt-injection"&#10; &#10; target="_blank" rel="noopener"&#10; &#10; &#10;&gt;prompt injection&lt;/a&gt;&#10;, the reviewer needs to identify whose text reaches the model and what an attacker can control. A returned document body or filename can carry instructions. For &lt;a&#10; href="https://genai.owasp.org/llmrisk/llm062025-excessive-agency"&#10; &#10; target="_blank" rel="noopener"&#10; &#10; &#10;&gt;excessive agency&lt;/a&gt;&#10;, the questions concern tool permissions, reachable side effects, and authorization enforced outside the model.&lt;/p&gt;&#10;&lt;p&gt;Those questions give the agent a path through the repository. It can follow a tool result back to its data source, then follow a tool call forward to the operation it performs. The skill also asks it to account for existing protections when assessing impact.&lt;/p&gt;&#10;&#10;&lt;h2 id="repeat-the-adaptation-for-ios"&gt;Repeat the adaptation for iOS &lt;/h2&gt;&#10;&lt;p&gt;Later, I applied the same approach to an iOS app using the &lt;a&#10; href="https://owasp.github.io/www-project-mobile-top-10"&#10; &#10; target="_blank" rel="noopener"&#10; &#10; &#10;&gt;OWASP Mobile Top 10 (2024)&lt;/a&gt;&#10;. That mobile security skill became part of production development.&lt;/p&gt;&#10;&lt;p&gt;The review method carried over. The mapping changed to platform APIs and the code responsible for them:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Mobile risk&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Concrete review question&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Improper credential usage&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Where are tokens stored, and which Keychain access settings apply?&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Insufficient input/output validation&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;What can a deep link or share-extension input cause the app to do?&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Insecure data storage&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Which protection classes apply to cached and temporary user files?&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Inadequate privacy controls&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Where can sensitive data appear outside its intended screen, including logs and notifications?&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Repository context matters here. If a shared SDK owns authentication or networking, the skill needs to direct the reviewer into that SDK. Stopping at the changed Swift view would leave the relevant behavior unexamined.&lt;/p&gt;&#10;&lt;p&gt;For a mobile adaptation, &lt;a&#10; href="https://mas.owasp.org/MASVS"&#10; &#10; target="_blank" rel="noopener"&#10; &#10; &#10;&gt;MASVS&lt;/a&gt;&#10; supplies verification controls, and &lt;a&#10; href="https://mas.owasp.org/MASTG"&#10; &#10; target="_blank" rel="noopener"&#10; &#10; &#10;&gt;MASTG&lt;/a&gt;&#10; provides testing guidance for checking them.&lt;/p&gt;&#10;&#10;&lt;h2 id="turn-company-values-into-review-rules"&gt;Turn company values into review rules &lt;/h2&gt;&#10;&lt;p&gt;Company values need concrete instructions too. For example, a company that prioritizes privacy can require reviewers to trace user data into logging, analytics, and crash reporting. That turns a broad preference into something an agent can inspect.&lt;/p&gt;&#10;&lt;p&gt;The mobile skill also records settled product decisions. An intentional tradeoff needs a rationale and the protections that still apply so that each review can assess the implementation against that decision. New evidence or changed assumptions can justify reopening it.&lt;/p&gt;&#10;&lt;p&gt;Severity needs the same local reasoning. OWASP provides risk categories; the product&amp;rsquo;s data, attacker access, and existing controls determine a finding&amp;rsquo;s impact.&lt;/p&gt;&#10;&lt;p&gt;A fresh adaptation request could be:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Use the existing security skill as a template. Keep its review workflow&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;and evidence requirements. Adapt the relevant OWASP categories to this&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;repository&amp;#39;s entry points, data flows, and platform APIs. Link each check&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;to the responsible code and a primary reference. Record product decisions&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;with their rationale. Preserve the language-level checks alongside it.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tailoring a security skill gives the agent concrete questions about the product it is building. Take one OWASP category in your current skill and map it to the code, data, and product decision the reviewer should examine.&lt;/p&gt;&#10;</description></item><item><title>A repeatable agentic development workflow: context, tickets, implementation, review</title><link>https://eghuro.cz/posts/agentic-development-workflow/</link><pubDate>Sun, 25 Oct 2026 18:25:00 +0100</pubDate><guid>https://eghuro.cz/posts/agentic-development-workflow/</guid><description>&lt;p&gt;Giving a coding agent a feature request and accepting the first working diff is easy. Repeating that across projects is where things get messy: the agent has to rediscover the domain, invent missing requirements, decide what “done” means, and review its own assumptions.&lt;/p&gt;&#10;&lt;p&gt;Here is the setup I now reuse across stacks. It has supported ten iOS app releases this year, helped clean up an existing app built with loose agent guidance, and helped a Go implementation recover after its first approach fell apart - to name a few examples. The Go work also involved a language stack and MCP technology I had not used before. The process didn&amp;rsquo;t provide that expertise on its own; it made the gaps visible early enough to address them.&lt;/p&gt;&#10;&#10;&lt;h2 id="give-the-agent-durable-context"&gt;Give the agent durable context &lt;/h2&gt;&#10;&lt;p&gt;Start with &lt;a&#10; href="https://github.com/mattpocock/skills"&#10; &#10; target="_blank" rel="noopener"&#10; &#10; &#10;&gt;mattpocock/skills&lt;/a&gt;&#10; and run its repository setup. Keep a short domain vocabulary in &lt;code&gt;CONTEXT.md&lt;/code&gt; (or the current upstream convention, &lt;code&gt;GLOSSARY.md&lt;/code&gt;). Record consequential design choices as ADRs. The agent can then use the same terms in discussions, tickets, code, and reviews.&lt;/p&gt;&#10;&lt;p&gt;For a change whose shape is uncertain, use &lt;code&gt;/grill-with-docs&lt;/code&gt; to work through the decisions and write them down. Run &lt;code&gt;/to-spec&lt;/code&gt; once the conversation has enough detail. Break the spec into small, ordered tickets with &lt;code&gt;/to-issues&lt;/code&gt; in older installations or &lt;code&gt;/to-tickets&lt;/code&gt; in the current upstream skills. Then use &lt;code&gt;/implement&lt;/code&gt; against those tickets.&lt;/p&gt;&#10;&lt;p&gt;The sequence is:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;shared vocabulary + ADRs → grill → spec → tickets → implement → review&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can enter the sequence at whichever step the work needs. A small, well-specified fix may already have a ticket. An uncertain feature should be grilled before an agent starts writing code.&lt;/p&gt;&#10;&#10;&lt;h2 id="put-tickets-where-the-project-works"&gt;Put tickets where the project works &lt;/h2&gt;&#10;&lt;p&gt;The tracker is an adapter for the process. One project keeps issues in a local &lt;code&gt;docs/TODO.txt&lt;/code&gt;; others use Vikunja or Taiga. What matters is that the agent knows where the authoritative spec and tickets live, how to record blockers, and how to update their status. Write those rules into the repository&amp;rsquo;s agent documentation. A tracker integration skill can handle the API details without changing the development sequence.&lt;/p&gt;&#10;&lt;p&gt;This also makes the setup portable. Moving to a new project means teaching the agent that project&amp;rsquo;s domain, tracker, and checks, rather than rebuilding the workflow.&lt;/p&gt;&#10;&#10;&lt;h2 id="make-implementation-answer-to-the-spec"&gt;Make implementation answer to the spec &lt;/h2&gt;&#10;&lt;p&gt;Implementation includes tests and a diff review. Review the result against two different questions: does the code follow the project&amp;rsquo;s standards, and does it actually satisfy the originating spec? The latter catches plausible code that solves the wrong problem. &lt;a&#10; href="https://eghuro.cz/posts/standards-vs-spec-review"&#10; &#10; data-rel="prefetch"&#10; &#10; &#10;&gt;A previous review&lt;/a&gt;&#10; found duplicate-write paths after a large test suite passed because it checked a safety claim against the call path.&lt;/p&gt;&#10;&lt;p&gt;Where the project&amp;rsquo;s rules allow it, use a different model family for review. A typical split is Claude writing the code and GPT reviewing it, since a different family brings different blind spots. Claude reviewing Claude has also proven decent, so one family is still a workable setup when you can only use one provider. For larger changes, a &lt;code&gt;multi-review&lt;/code&gt; skill can run distinct axes such as correctness, quality, tests and performance, security, and, for frontend, accessibility. Give each reviewer the same bounded diff and the actual spec; collect actionable findings back into the tracker.&lt;/p&gt;&#10;&lt;p&gt;Attach stack-specific skills where they help. Go work can load guidance for concurrency, error handling, testing, and security. iOS work can use Swift and mobile guidance. For exposed web APIs, mobile clients, or MCP tools, apply relevant OWASP-based web, mobile, and LLM security skills during implementation and review. A checklist gives the agent concrete questions to ask; it does not turn a code review into a security certification.&lt;/p&gt;&#10;&lt;p&gt;Start with one change: write down the vocabulary and decision, turn the request into a spec and tickets, implement one ticket, and review the diff against the spec and its security boundaries. Adjust the tracker, skills, and review axes to fit the next project.&lt;/p&gt;&#10;</description></item><item><title>Standards vs Spec review: a second agent found duplicate writes past 1,572 green tests</title><link>https://eghuro.cz/posts/standards-vs-spec-review/</link><pubDate>Wed, 22 Jul 2026 21:58:00 +0200</pubDate><guid>https://eghuro.cz/posts/standards-vs-spec-review/</guid><description>&lt;p&gt;This is what the review agent reported, just before it listed its findings:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;The test evidence is clean: Ruff passes, Python reports 1,572 passed / 56 skipped /&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1 expected failure, and the GUI reports 418 passed / 4 skipped. The review nevertheless&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;found safety gaps the tests do not exercise — most importantly, two write clients retry&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;500-after-commit&amp;#34; before their advertised idempotency/verification guard can run.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A coding agent had built the branch: 53 commits, 147 files. A different model reviewed it and found two paths that could put the same invoice in the ERP twice and report plain success. No test covered them, and no linter could. They came from a reviewer with one job: check the code against what the branch said it does.&lt;/p&gt;&#10;&#10;&lt;h2 id="the-setup"&gt;The setup &lt;/h2&gt;&#10;&lt;p&gt;The repository vendors the two-axis &lt;code&gt;code-review&lt;/code&gt; skill from &lt;a&#10; href="https://github.com/mattpocock/skills"&#10; &#10; target="_blank" rel="noopener"&#10; &#10; &#10;&gt;mattpocock/skills&lt;/a&gt;&#10;. It pins a fixed point and diffs against the merge base, &lt;code&gt;git diff master...HEAD&lt;/code&gt;, then forks two subagents in parallel:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Standards: does the diff follow the repo&amp;rsquo;s documented rules (glossary, ADRs, a baseline of Fowler code smells)?&lt;/li&gt;&#10;&lt;li&gt;Spec: does the diff do what the originating issue or PRD asked for?&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;The parent agent runs lint and tests meanwhile, &amp;ldquo;as supporting evidence, not as a substitute for the diff review&amp;rdquo;. It then reports the two axes under separate headings and doesn&amp;rsquo;t rerank them, so a pile of naming nits can&amp;rsquo;t bury a P1.&lt;/p&gt;&#10;&lt;p&gt;The Spec brief is short:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Report: (a) requirements the spec asked for that are missing or partial;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(b) behavior in the diff that wasn&amp;#39;t asked for (scope creep);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(c) requirements that look implemented but where the implementation looks wrong.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Quote the spec line for each finding, under 400 words.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;h2 id="what-each-axis-found"&gt;What each axis found &lt;/h2&gt;&#10;&lt;p&gt;There was no PRD for the branch, so the Spec reviewer used the commit messages and the ADRs. One commit claimed that the MCP server&amp;rsquo;s idempotency check made it safe to retry a write that got a 5xx. The reviewer traced the call: the check ran before &lt;code&gt;_request()&lt;/code&gt;, the key was recorded after success, and the retry happened inside &lt;code&gt;_request()&lt;/code&gt;, in between. A webhook worker had the same shape, with a verify-by-GET that ran only if the request finally raised. A retry that succeeds never raises. (&lt;a&#10; href="https://eghuro.cz/posts/http-500-ambiguous-write-retry"&#10; &#10; data-rel="prefetch"&#10; &#10; &#10;&gt;The full HTTP 500 story is here&lt;/a&gt;&#10;.)&lt;/p&gt;&#10;&lt;p&gt;The Standards reviewer found something else. Two functions called &lt;code&gt;urlopen()&lt;/code&gt; directly, past the shared transport that an ADR says owns all retries. It also found an import that went through a module the ADRs forbid as a re-export facade, and an unfinished vocabulary migration.&lt;/p&gt;&#10;&lt;p&gt;None of the Standards findings overlapped with the Spec ones. All six were fixed the same evening, in one commit.&lt;/p&gt;&#10;&#10;&lt;h2 id="why-the-tests-stayed-green"&gt;Why the tests stayed green &lt;/h2&gt;&#10;&lt;p&gt;The tests came from the same author as the claim. No test let a 500 commit the write, let the retry succeed, and counted the writes, because the commit said the idempotency check covered that case. A suite tests what its author thought could break.&lt;/p&gt;&#10;&lt;p&gt;The Spec reviewer starts from the claim instead. A commit message says the code is safe, so it goes looking for the line that makes it safe, and here that line ran on the wrong side of the retry.&lt;/p&gt;&#10;&#10;&lt;h2 id="two-more-runs-same-pattern"&gt;Two more runs, same pattern &lt;/h2&gt;&#10;&lt;p&gt;In the next review, a few hours later on a six-commit range, a guard was undone downstream. Every write had to carry exactly one confidence label, and &lt;code&gt;create_*&lt;/code&gt; checked that. But when label setup had failed, a fallback further down stripped all labels from the payload. The reviewer reproduced a labeled invoice leaving without its label. It also found a write that went through a raw &lt;code&gt;put()&lt;/code&gt; and skipped the scope check that limits which record types may be written at all.&lt;/p&gt;&#10;&lt;p&gt;The third review had nothing to go on but commit messages. The branch added a gate: if an invoice is addressed to another company, warn and refuse to post it. The reviewer said it was inferring the spec from the commits, so I pasted the original request and the Spec subagent re-checked against that. The request held a second requirement the commit summary had left out, which turned out to be implemented. The review also found that a replay of staged payloads called the push function directly and skipped the new gate. And while company registration numbers were checksum-validated, VAT IDs relied on regex alone, so one misread digit could block a legitimate invoice. Both failed the first test, then got fixed.&lt;/p&gt;&#10;&#10;&lt;h2 id="how-to-use-it"&gt;How to use it &lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Run Standards and Spec as separate subagents, so neither sees the other&amp;rsquo;s findings, and report them under separate headings.&lt;/li&gt;&#10;&lt;li&gt;Give the Spec reviewer the authoritative spec. If you only have commit messages, say so, and swap in the real request the moment you have it.&lt;/li&gt;&#10;&lt;li&gt;Treat claims in commit messages and ADRs (&amp;ldquo;X makes this safe&amp;rdquo;, &amp;ldquo;Y owns all retries&amp;rdquo;) as requirements. They are the cheapest spec you have, and the author&amp;rsquo;s tests already agree with them.&lt;/li&gt;&#10;&lt;li&gt;If you can, review with a different model than the one that wrote the code. It is less likely to share the writer&amp;rsquo;s blind spots.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&#10;&lt;h2 id="takeaway"&gt;Takeaway &lt;/h2&gt;&#10;&lt;p&gt;A green suite tells you the code does what its author expected. A Spec review by a second agent tells you whether that matches what the author promised. The duplicate writes were in the gap between the two.&lt;/p&gt;&#10;&lt;p&gt;Take the last branch your agent wrote, and have a second agent check each &amp;ldquo;this makes X safe&amp;rdquo; in its commit messages against the code.&lt;/p&gt;&#10;</description></item></channel></rss>