<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ling&#039;s Note</title>
	<atom:link href="https://www.chunho-ling.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.chunho-ling.com</link>
	<description>Everything related IT, and me.</description>
	<lastBuildDate>Fri, 15 May 2026 09:31:24 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>
<site xmlns="com-wordpress:feed-additions:1">104401516</site>	<item>
		<title>AI vs DevOps? Automate smarter, safer.</title>
		<link>https://www.chunho-ling.com/ai-vs-devops-automate-smarter-safer/</link>
					<comments>https://www.chunho-ling.com/ai-vs-devops-automate-smarter-safer/#respond</comments>
		
		<dc:creator><![CDATA[C.H. Ling]]></dc:creator>
		<pubDate>Fri, 15 May 2026 09:31:24 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[Computing]]></category>
		<guid isPermaLink="false">https://www.chunho-ling.com/?p=1839</guid>

					<description><![CDATA[Every few years, a new technology is predicted to kill DevOps. Cloud was supposed to do it. Kubernetes was supposed to do it. Serverless was supposed to do it. Platform engineering was supposed to do <a class="mh-excerpt-more" href="https://www.chunho-ling.com/ai-vs-devops-automate-smarter-safer/" title="AI vs DevOps? Automate smarter, safer.">[...]</a>]]></description>
										<content:encoded><![CDATA[<p data-start="3071" data-end="3133">Every few years, a new technology is predicted to kill DevOps.</p>
<p data-start="3135" data-end="3315">Cloud was supposed to do it. Kubernetes was supposed to do it. Serverless was supposed to do it. Platform engineering was supposed to do it. Now AI agents are the latest candidate.</p>
<p data-start="3317" data-end="3344">The question sounds simple:</p>
<blockquote data-start="3346" data-end="3372">
<p data-start="3348" data-end="3372"><strong data-start="3348" data-end="3372">Will AI kill DevOps?</strong></p>
</blockquote>
<p data-start="3374" data-end="3397">The better question is:</p>
<blockquote data-start="3399" data-end="3483">
<p data-start="3401" data-end="3483"><strong data-start="3401" data-end="3483">Which parts of DevOps become automated, and which parts become more important?</strong></p>
</blockquote>
<p data-start="3485" data-end="3713">DevOps has always been about automation, feedback loops, reliable delivery, and reducing manual handoff between development and operations. AI agents are not the opposite of DevOps. They are a continuation of the same direction.</p>
<p data-start="3715" data-end="3776">The difference is that AI can now reason across more context:</p>
<ul data-start="3778" data-end="3981">
<li data-section-id="10oh7ug" data-start="3778" data-end="3791">source code</li>
<li data-section-id="12fqovj" data-start="3792" data-end="3807">pull requests</li>
<li data-section-id="ivpt31" data-start="3808" data-end="3823">pipeline logs</li>
<li data-section-id="vp4740" data-start="3824" data-end="3841">cloud resources</li>
<li data-section-id="jge3h4" data-start="3842" data-end="3864">infrastructure state</li>
<li data-section-id="818a73" data-start="3865" data-end="3874">metrics</li>
<li data-section-id="1kz9w1m" data-start="3875" data-end="3883">traces</li>
<li data-section-id="626499" data-start="3884" data-end="3895">incidents</li>
<li data-section-id="13blffj" data-start="3896" data-end="3905">tickets</li>
<li data-section-id="11qqogq" data-start="3906" data-end="3923">vulnerabilities</li>
<li data-section-id="e6p7n2" data-start="3924" data-end="3941">access requests</li>
<li data-section-id="15u2ikb" data-start="3942" data-end="3952">runbooks</li>
<li data-section-id="10udir3" data-start="3953" data-end="3981">architecture documentation</li>
</ul>
<p data-start="3983" data-end="4042">That creates real opportunities. It also creates new risks.</p>
<p data-start="4044" data-end="4200">The future is not DevOps disappearing. The future is DevOps becoming more automated, more policy-driven, and more dependent on strong engineering judgement.</p>
<hr data-start="4202" data-end="4205" />
<h2 data-section-id="1u9nex9" data-start="4207" data-end="4226">Why This Matters</h2>
<p data-start="4228" data-end="4303">Many DevOps teams still spend too much time on repetitive operational work:</p>
<ul data-start="4305" data-end="4543">
<li data-section-id="yw73u9" data-start="4305" data-end="4330">fixing broken pipelines</li>
<li data-section-id="1s6yoie" data-start="4331" data-end="4355">checking logs manually</li>
<li data-section-id="1qql4zv" data-start="4356" data-end="4374">updating tickets</li>
<li data-section-id="1vctldm" data-start="4375" data-end="4401">applying routine patches</li>
<li data-section-id="k9n590" data-start="4402" data-end="4429">reviewing access requests</li>
<li data-section-id="5ebilk" data-start="4430" data-end="4457">collecting audit evidence</li>
<li data-section-id="1wsrxbz" data-start="4458" data-end="4486">investigating noisy alerts</li>
<li data-section-id="1ahujvf" data-start="4487" data-end="4543">running the same operational checklist again and again</li>
</ul>
<p data-start="4545" data-end="4753">Google’s SRE guidance describes toil as repetitive, predictable work related to maintaining a service, and argues that reducing toil is central to operational efficiency.</p>
<p data-start="4755" data-end="4788">This is where AI agents can help.</p>
<p data-start="4790" data-end="5115">AI is good at reading context, summarising information, identifying patterns, generating draft changes, and calling tools through controlled interfaces. When connected to APIs, CI/CD systems, observability platforms, security scanners, ticketing systems, and infrastructure tools, AI can reduce a lot of operational friction.</p>
<p data-start="5117" data-end="5167">But AI only works safely when the environment has:</p>
<ul data-start="5169" data-end="5324">
<li data-section-id="1dcqqvu" data-start="5169" data-end="5181">clear APIs</li>
<li data-section-id="x5im7" data-start="5182" data-end="5202">reliable telemetry</li>
<li data-section-id="akirp1" data-start="5203" data-end="5224">documented runbooks</li>
<li data-section-id="2hwq3g" data-start="5225" data-end="5242">policy controls</li>
<li data-section-id="n7vpbj" data-start="5243" data-end="5263">approval workflows</li>
<li data-section-id="10d4r5y" data-start="5264" data-end="5279">audit logging</li>
<li data-section-id="12dmt91" data-start="5280" data-end="5302">ownership boundaries</li>
<li data-section-id="1blg0m8" data-start="5303" data-end="5324">rollback procedures</li>
</ul>
<p data-start="5326" data-end="5400">Without these, AI automation can become another source of production risk.</p>
<blockquote data-start="5402" data-end="5523">
<p data-start="5404" data-end="5523"><strong data-start="5404" data-end="5427">Practical takeaway:</strong><br data-start="5427" data-end="5430" />AI does not remove the need for DevOps maturity. It increases the value of DevOps maturity.</p>
</blockquote>
<hr data-start="5525" data-end="5528" />
<h2 data-section-id="1d1h5zw" data-start="5530" data-end="5605">Core Concept: AI Does Not Replace DevOps, It Changes the Operating Model</h2>
<p data-start="5607" data-end="5740">DevOps is not only a collection of tools. It is a way of delivering and running software with speed, reliability, and accountability.</p>
<p data-start="5742" data-end="5816">AI can automate parts of the toolchain, but it cannot remove the need for:</p>
<ul data-start="5818" data-end="5983">
<li data-section-id="1t11wi3" data-start="5818" data-end="5829">ownership</li>
<li data-section-id="1ibuila" data-start="5830" data-end="5854">architecture decisions</li>
<li data-section-id="1j6bnhk" data-start="5855" data-end="5882">production accountability</li>
<li data-section-id="3u8drs" data-start="5883" data-end="5900">risk management</li>
<li data-section-id="ep6c34" data-start="5901" data-end="5922">security governance</li>
<li data-section-id="7aagva" data-start="5923" data-end="5944">compliance evidence</li>
<li data-section-id="1mgszv9" data-start="5945" data-end="5965">incident judgement</li>
<li data-section-id="1sy3xmt" data-start="5966" data-end="5983">platform design</li>
</ul>
<p data-start="5985" data-end="6016">The more realistic position is:</p>
<blockquote data-start="6018" data-end="6090">
<p data-start="6020" data-end="6090"><strong data-start="6020" data-end="6090">AI will not replace mature DevOps. It will expose immature DevOps.</strong></p>
</blockquote>
<p data-start="6092" data-end="6393">Teams that depend on manual tickets, tribal knowledge, undocumented scripts, weak observability, and reactive firefighting will be vulnerable to disruption. Teams that already have strong CI/CD, infrastructure as code, observability, SRE practices, and security controls will be able to use AI safely.</p>
<hr data-start="6395" data-end="6398" />
<h2 data-section-id="p07olx" data-start="6400" data-end="6437">What AI Actually Changes in DevOps</h2>
<div class="TyagGW_tableContainer">
<div class="group TyagGW_tableWrapper flex flex-col-reverse w-fit" tabindex="-1">
<table class="w-fit min-w-(--thread-content-width)" data-start="6439" data-end="7247">
<thead data-start="6439" data-end="6494">
<tr data-start="6439" data-end="6494">
<th class="last:pe-10" data-start="6439" data-end="6453" data-col-size="sm">DevOps Area</th>
<th class="last:pe-10" data-start="6453" data-end="6473" data-col-size="md">Traditional Model</th>
<th class="last:pe-10" data-start="6473" data-end="6494" data-col-size="md">AI-Assisted Model</th>
</tr>
</thead>
<tbody data-start="6509" data-end="7247">
<tr data-start="6509" data-end="6624">
<td data-start="6509" data-end="6517" data-col-size="sm">CI/CD</td>
<td data-col-size="md" data-start="6517" data-end="6564">Engineers maintain pipeline scripts manually</td>
<td data-col-size="md" data-start="6564" data-end="6624">Agents generate, explain, repair, and optimise pipelines</td>
</tr>
<tr data-start="6625" data-end="6735">
<td data-start="6625" data-end="6631" data-col-size="sm">IaC</td>
<td data-col-size="md" data-start="6631" data-end="6677">Humans write and review infrastructure code</td>
<td data-col-size="md" data-start="6677" data-end="6735">Agents detect drift, review plans, and propose changes</td>
</tr>
<tr data-start="6736" data-end="6831">
<td data-start="6736" data-end="6749" data-col-size="sm">Monitoring</td>
<td data-col-size="md" data-start="6749" data-end="6773">Teams react to alerts</td>
<td data-col-size="md" data-start="6773" data-end="6831">Agents correlate signals and suggest preventive action</td>
</tr>
<tr data-start="6832" data-end="6941">
<td data-start="6832" data-end="6838" data-col-size="sm">SRE</td>
<td data-start="6838" data-end="6878" data-col-size="md">Engineers diagnose incidents manually</td>
<td data-col-size="md" data-start="6878" data-end="6941">Agents assist with triage, runbooks, and incident summaries</td>
</tr>
<tr data-start="6942" data-end="7044">
<td data-start="6942" data-end="6953" data-col-size="sm">Security</td>
<td data-start="6953" data-end="6989" data-col-size="md">Periodic scans and manual reviews</td>
<td data-col-size="md" data-start="6989" data-end="7044">Continuous vulnerability, access, and policy review</td>
</tr>
<tr data-start="7045" data-end="7151">
<td data-start="7045" data-end="7055" data-col-size="sm">Support</td>
<td data-col-size="md" data-start="7055" data-end="7091">Tickets routed to human operators</td>
<td data-col-size="md" data-start="7091" data-end="7151">Agents handle standard workflows and escalate exceptions</td>
</tr>
<tr data-start="7152" data-end="7247">
<td data-start="7152" data-end="7165" data-col-size="sm">Governance</td>
<td data-start="7165" data-end="7194" data-col-size="md">Manual evidence collection</td>
<td data-col-size="md" data-start="7194" data-end="7247">Automated audit summaries and compliance evidence</td>
</tr>
</tbody>
</table>
</div>
</div>
<hr data-start="7249" data-end="7252" />
<h2 data-section-id="d3rbrq" data-start="7254" data-end="7314">1. CI/CD: From Pipeline Scripts to Delivery Orchestration</h2>
<p data-start="7316" data-end="7378">CI/CD is one of the most obvious areas for AI-assisted DevOps.</p>
<p data-start="7380" data-end="7635">Today, many teams still maintain complex YAML pipelines manually. Build failures are inspected by reading logs. Release notes are prepared manually. Deployment evidence is scattered across source control, CI/CD systems, ticketing tools, and chat messages.</p>
<p data-start="7637" data-end="7673">AI agents can improve this workflow.</p>
<h3 data-section-id="1w0yzqq" data-start="7675" data-end="7705">AI can help with CI/CD by:</h3>
<ul data-start="7707" data-end="8021">
<li data-section-id="gyj8w7" data-start="7707" data-end="7738">generating pipeline templates</li>
<li data-section-id="axbktl" data-start="7739" data-end="7765">explaining failed builds</li>
<li data-section-id="10673aa" data-start="7766" data-end="7793">summarising test failures</li>
<li data-section-id="elnn4g" data-start="7794" data-end="7819">identifying flaky tests</li>
<li data-section-id="hdd92n" data-start="7820" data-end="7847">suggesting pipeline fixes</li>
<li data-section-id="1u96x0n" data-start="7848" data-end="7879">checking deployment readiness</li>
<li data-section-id="1ymgckm" data-start="7880" data-end="7905">preparing release notes</li>
<li data-section-id="3vxycl" data-start="7906" data-end="7941">creating rollback recommendations</li>
<li data-section-id="gjjcek" data-start="7942" data-end="7971">collecting release evidence</li>
<li data-section-id="1rlz9jt" data-start="7972" data-end="8021">opening pull requests for pipeline improvements</li>
</ul>
<p data-start="8023" data-end="8322">MCP is relevant here because it provides a standard way for AI applications to integrate with external tools and data sources. The official MCP specification describes it as an open protocol for integrating LLM applications with external data sources and tools.</p>
<p data-start="8324" data-end="8399">In a DevOps environment, MCP-style tools could expose controlled access to:</p>
<ul data-start="8401" data-end="8549">
<li data-section-id="wilm91" data-start="8401" data-end="8419">GitHub or GitLab</li>
<li data-section-id="4u2sxi" data-start="8420" data-end="8429">Jenkins</li>
<li data-section-id="1hb2fca" data-start="8430" data-end="8442">Kubernetes</li>
<li data-section-id="p3c6lb" data-start="8443" data-end="8460">Terraform Cloud</li>
<li data-section-id="116tjn" data-start="8461" data-end="8482">cloud provider APIs</li>
<li data-section-id="4btrda" data-start="8483" data-end="8503">Jira or ServiceNow</li>
<li data-section-id="1c64h2x" data-start="8504" data-end="8529">observability platforms</li>
<li data-section-id="16ishjf" data-start="8530" data-end="8549">security scanners</li>
</ul>
<p data-start="8551" data-end="8608">However, CI/CD should not be fully replaced by AI agents.</p>
<p data-start="8610" data-end="8665">CI/CD still needs deterministic and auditable controls:</p>
<ul data-start="8667" data-end="8852">
<li data-section-id="ovj790" data-start="8667" data-end="8698">repeatable workflow execution</li>
<li data-section-id="1tz3n3v" data-start="8699" data-end="8716">automated tests</li>
<li data-section-id="ii7fgr" data-start="8717" data-end="8733">approval gates</li>
<li data-section-id="82ro95" data-start="8734" data-end="8752">artefact signing</li>
<li data-section-id="1r52gg9" data-start="8753" data-end="8775">environment controls</li>
<li data-section-id="dbde37" data-start="8776" data-end="8796">deployment history</li>
<li data-section-id="ekinbk" data-start="8797" data-end="8813">rollback logic</li>
<li data-section-id="kjnipz" data-start="8814" data-end="8837">segregation of duties</li>
<li data-section-id="86ieic" data-start="8838" data-end="8852">audit trails</li>
</ul>
<blockquote data-start="8854" data-end="8963">
<p data-start="8856" data-end="8963"><strong data-start="8856" data-end="8879">Practical takeaway:</strong><br data-start="8879" data-end="8882" />AI should assist the delivery system. It should not become the delivery system.</p>
</blockquote>
<hr data-start="8965" data-end="8968" />
<h2 data-section-id="1739dt5" data-start="8970" data-end="9024">2. Infrastructure as Code: AI Will Not Remove State</h2>
<p data-start="9026" data-end="9186">One tempting argument is that AI agents can scan cloud infrastructure through APIs, store the current status in memory, and remove the need for Terraform state.</p>
<p data-start="9188" data-end="9218">That is not a safe conclusion.</p>
<p data-start="9220" data-end="9459">Terraform state is not just a cache. HashiCorp explains that Terraform state is necessary because it maps real-world resources to Terraform configuration and helps Terraform understand what it manages.</p>
<p data-start="9461" data-end="9532">Cloud API discovery can show what exists, but it cannot always explain:</p>
<ul data-start="9534" data-end="9814">
<li data-section-id="v0t739" data-start="9534" data-end="9557">why a resource exists</li>
<li data-section-id="1sq8l40" data-start="9558" data-end="9571">who owns it</li>
<li data-section-id="1tpdg53" data-start="9572" data-end="9599">whether it is intentional</li>
<li data-section-id="gxlhv6" data-start="9600" data-end="9625">which module created it</li>
<li data-section-id="1hqhp6w" data-start="9626" data-end="9656">whether it should be changed</li>
<li data-section-id="1fsaefl" data-start="9657" data-end="9682">whether it is compliant</li>
<li data-section-id="scxi1" data-start="9683" data-end="9733">whether it is manually created or managed by IaC</li>
<li data-section-id="en04tl" data-start="9734" data-end="9771">what dependency relationship exists</li>
<li data-section-id="efnwal" data-start="9772" data-end="9814">what the intended architecture should be</li>
</ul>
<p data-start="9816" data-end="9895">AI memory is also not a safe replacement for infrastructure state. It may lack:</p>
<ul data-start="9897" data-end="10026">
<li data-section-id="zf1oar" data-start="9897" data-end="9906">locking</li>
<li data-section-id="9kn44m" data-start="9907" data-end="9920">consistency</li>
<li data-section-id="1rdy4y" data-start="9921" data-end="9933">versioning</li>
<li data-section-id="40nicv" data-start="9934" data-end="9950">reconciliation</li>
<li data-section-id="1qrgemi" data-start="9951" data-end="9967">drift tracking</li>
<li data-section-id="17qxji1" data-start="9968" data-end="9992">deterministic planning</li>
<li data-section-id="go7e4n" data-start="9993" data-end="10007">auditability</li>
<li data-section-id="w9v7jl" data-start="10008" data-end="10026">rollback support</li>
</ul>
<p data-start="10028" data-end="10125">That does not mean AI has no role in IaC. It has a strong role, but not as a hidden state engine.</p>
<h3 data-section-id="d5myay" data-start="10127" data-end="10158">Better AI use cases for IaC</h3>
<p data-start="10160" data-end="10177">AI can help with:</p>
<ul data-start="10179" data-end="10541">
<li data-section-id="5ffktl" data-start="10179" data-end="10209">generating Terraform modules</li>
<li data-section-id="gs35pw" data-start="10210" data-end="10237">reviewing Terraform plans</li>
<li data-section-id="quysyv" data-start="10238" data-end="10279">explaining risky infrastructure changes</li>
<li data-section-id="1n4zxoi" data-start="10280" data-end="10297">detecting drift</li>
<li data-section-id="v2bjlk" data-start="10298" data-end="10334">comparing cloud inventory with IaC</li>
<li data-section-id="fukyfj" data-start="10335" data-end="10372">creating pull requests to fix drift</li>
<li data-section-id="1ts4kao" data-start="10373" data-end="10401">documenting infrastructure</li>
<li data-section-id="1pvrcmh" data-start="10402" data-end="10432">identifying unused resources</li>
<li data-section-id="193g94j" data-start="10433" data-end="10461">checking tagging standards</li>
<li data-section-id="1jxok0i" data-start="10462" data-end="10486">estimating cost impact</li>
<li data-section-id="1ln8ft7" data-start="10487" data-end="10541">reviewing IAM, security groups, and network exposure</li>
</ul>
<blockquote data-start="10543" data-end="10623">
<p data-start="10545" data-end="10623"><strong data-start="10545" data-end="10554">Note:</strong><br data-start="10554" data-end="10557" />AI should improve IaC workflows, not bypass the source of truth.</p>
</blockquote>
<hr data-start="10625" data-end="10628" />
<h2 data-section-id="wtfhwl" data-start="10630" data-end="10694">3. Monitoring and SRE: From Reactive to Preventive Operations</h2>
<p data-start="10696" data-end="10751">Traditional operations often follow a reactive pattern:</p>
<ol data-start="10753" data-end="10975">
<li data-section-id="opgjrl" data-start="10753" data-end="10768">Alert fires.</li>
<li data-section-id="1vgyubi" data-start="10769" data-end="10800">Engineer checks a dashboard.</li>
<li data-section-id="drmd6o" data-start="10801" data-end="10827">Engineer searches logs.</li>
<li data-section-id="1kd6hz0" data-start="10828" data-end="10866">Engineer checks recent deployments.</li>
<li data-section-id="hto3fa" data-start="10867" data-end="10906">Engineer updates an incident ticket.</li>
<li data-section-id="19udym4" data-start="10907" data-end="10945">Engineer escalates to another team.</li>
<li data-section-id="89sogn" data-start="10946" data-end="10975">Root cause is found later.</li>
</ol>
<p data-start="10977" data-end="11043">AI can improve this pattern by correlating signals across systems.</p>
<h3 data-section-id="ing548" data-start="11045" data-end="11071">AI can support SRE by:</h3>
<ul data-start="11073" data-end="11426">
<li data-section-id="1xzmqyc" data-start="11073" data-end="11133">correlating metrics, logs, traces, events, and deployments</li>
<li data-section-id="1xg4qma" data-start="11134" data-end="11172">detecting abnormal behaviour earlier</li>
<li data-section-id="yeou7e" data-start="11173" data-end="11204">identifying saturation trends</li>
<li data-section-id="1h65gxe" data-start="11205" data-end="11238">highlighting likely root causes</li>
<li data-section-id="1s1tyzh" data-start="11239" data-end="11261">reducing alert noise</li>
<li data-section-id="1u4z2rl" data-start="11262" data-end="11290">suggesting runbook actions</li>
<li data-section-id="q26j1z" data-start="11291" data-end="11320">creating incident timelines</li>
<li data-section-id="bj1mbr" data-start="11321" data-end="11353">drafting post-incident reviews</li>
<li data-section-id="1f38ftp" data-start="11354" data-end="11385">recommending capacity changes</li>
<li data-section-id="101byu" data-start="11386" data-end="11426">identifying recurring failure patterns</li>
</ul>
<p data-start="11428" data-end="11465">This is where AIOps becomes relevant.</p>
<p data-start="11467" data-end="11632">AIOps means using AI and analytics to improve IT operations. It is commonly applied to monitoring, event correlation, diagnosis, and operational workflow automation.</p>
<p data-start="11634" data-end="11719">However, AI cannot compensate for poor observability. It needs good operational data.</p>
<h3 data-section-id="4qc63u" data-start="11721" data-end="11747">AI-assisted SRE needs:</h3>
<ul data-start="11749" data-end="11922">
<li data-section-id="1yvwe83" data-start="11749" data-end="11765">useful metrics</li>
<li data-section-id="1u1nhxq" data-start="11766" data-end="11783">structured logs</li>
<li data-section-id="rd3szt" data-start="11784" data-end="11804">distributed traces</li>
<li data-section-id="t78lli" data-start="11805" data-end="11824">service ownership</li>
<li data-section-id="1a4q1xk" data-start="11825" data-end="11842">dependency maps</li>
<li data-section-id="1j4cr7v" data-start="11843" data-end="11849">SLOs</li>
<li data-section-id="15u2ikb" data-start="11850" data-end="11860">runbooks</li>
<li data-section-id="1f4v4gb" data-start="11861" data-end="11882">known failure modes</li>
<li data-section-id="dbde37" data-start="11883" data-end="11903">deployment history</li>
<li data-section-id="1t707bw" data-start="11904" data-end="11922">incident history</li>
</ul>
<blockquote data-start="11924" data-end="12075">
<p data-start="11926" data-end="12075"><strong data-start="11926" data-end="11949">Practical takeaway:</strong><br data-start="11949" data-end="11952" />AI can help teams move from reactive firefighting to preventive operations, but only if the operational data is reliable.</p>
</blockquote>
<hr data-start="12077" data-end="12080" />
<h2 data-section-id="17csg7b" data-start="12082" data-end="12122">4. Security: Agent-Assisted DevSecOps</h2>
<p data-start="12124" data-end="12179">Security is another strong area for AI-assisted DevOps.</p>
<p data-start="12181" data-end="12248">Modern security work is fragmented across many tools and workflows:</p>
<ul data-start="12250" data-end="12453">
<li data-section-id="175xoz6" data-start="12250" data-end="12271">dependency scanners</li>
<li data-section-id="vf30m5" data-start="12272" data-end="12298">container image scanners</li>
<li data-section-id="1k2sd14" data-start="12299" data-end="12317">secrets scanners</li>
<li data-section-id="1rqm0yj" data-start="12318" data-end="12331">IAM systems</li>
<li data-section-id="vez0hy" data-start="12332" data-end="12349">CI/CD platforms</li>
<li data-section-id="12naglg" data-start="12350" data-end="12372">cloud security tools</li>
<li data-section-id="1mf7aai" data-start="12373" data-end="12392">ticketing systems</li>
<li data-section-id="qp29yi" data-start="12393" data-end="12418">vulnerability databases</li>
<li data-section-id="1ijup6q" data-start="12419" data-end="12453">compliance evidence repositories</li>
</ul>
<p data-start="12455" data-end="12496">AI agents can help connect these signals.</p>
<h3 data-section-id="vgxixu" data-start="12498" data-end="12529">AI can assist DevSecOps by:</h3>
<ul data-start="12531" data-end="12904">
<li data-section-id="1txekb0" data-start="12531" data-end="12564">checking vulnerability findings</li>
<li data-section-id="4mjdt8" data-start="12565" data-end="12592">analysing dependency risk</li>
<li data-section-id="1symkgl" data-start="12593" data-end="12617">summarising CVE impact</li>
<li data-section-id="1gcg0n4" data-start="12618" data-end="12648">creating patch pull requests</li>
<li data-section-id="ryuqbb" data-start="12649" data-end="12689">reviewing container image scan results</li>
<li data-section-id="5rb9g3" data-start="12690" data-end="12716">checking IAM permissions</li>
<li data-section-id="14wyg2c" data-start="12717" data-end="12755">detecting over-permissioned accounts</li>
<li data-section-id="11nzx3m" data-start="12756" data-end="12786">identifying exposed services</li>
<li data-section-id="p5zbr6" data-start="12787" data-end="12814">reviewing Kubernetes RBAC</li>
<li data-section-id="rx7jy" data-start="12815" data-end="12846">checking CI/CD pipeline risks</li>
<li data-section-id="1n1b1xu" data-start="12847" data-end="12873">preparing audit evidence</li>
<li data-section-id="1vnakut" data-start="12874" data-end="12904">tracking security exceptions</li>
</ul>
<p data-start="12906" data-end="13139">CI/CD security should be treated as a first-class concern. OWASP maintains a dedicated Top 10 list for CI/CD security risks, covering risks and recommended controls for modern delivery pipelines.</p>
<p data-start="13141" data-end="13225">AI can make this better, but also more dangerous if permissions are poorly designed.</p>
<p data-start="13227" data-end="13324">An AI agent should not automatically perform high-risk security actions without control, such as:</p>
<ul data-start="13326" data-end="13522">
<li data-section-id="14z2awd" data-start="13326" data-end="13349">granting admin access</li>
<li data-section-id="1j7eez6" data-start="13350" data-end="13379">rotating production secrets</li>
<li data-section-id="9y6a48" data-start="13380" data-end="13405">changing firewall rules</li>
<li data-section-id="s6lcbq" data-start="13406" data-end="13425">deleting accounts</li>
<li data-section-id="btwa1t" data-start="13426" data-end="13469">patching critical systems without testing</li>
<li data-section-id="1skj980" data-start="13470" data-end="13501">approving security exceptions</li>
<li data-section-id="1y6tzql" data-start="13502" data-end="13522">disabling controls</li>
</ul>
<blockquote data-start="13524" data-end="13655">
<p data-start="13526" data-end="13655"><strong data-start="13526" data-end="13549">Practical takeaway:</strong><br data-start="13549" data-end="13552" />Agent-assisted DevSecOps is valuable, but an over-permissioned AI agent becomes a new attack surface.</p>
</blockquote>
<hr data-start="13657" data-end="13660" />
<h2 data-section-id="fhaf91" data-start="13662" data-end="13727">5. Chaos Engineering: AI Can Help, But Should Not Act Randomly</h2>
<p data-start="13729" data-end="13796">The correct term is <strong data-start="13749" data-end="13770">chaos engineering</strong>, not “caros engineering.”</p>
<p data-start="13798" data-end="14017">Chaos engineering is about testing system resilience by introducing controlled failure scenarios. AI can assist by identifying weak points and proposing experiments, but it should not randomly execute destructive tests.</p>
<h3 data-section-id="pb7yjj" data-start="14019" data-end="14061">AI can help with chaos engineering by:</h3>
<ul data-start="14063" data-end="14328">
<li data-section-id="6ao9a8" data-start="14063" data-end="14101">identifying single points of failure</li>
<li data-section-id="19eyyp" data-start="14102" data-end="14135">reviewing architecture diagrams</li>
<li data-section-id="hs0il0" data-start="14136" data-end="14165">proposing failure scenarios</li>
<li data-section-id="khod8x" data-start="14166" data-end="14197">checking whether alerts exist</li>
<li data-section-id="1xkh3ih" data-start="14198" data-end="14232">checking whether rollback exists</li>
<li data-section-id="16h7jzb" data-start="14233" data-end="14262">generating experiment plans</li>
<li data-section-id="l2uob" data-start="14263" data-end="14289">summarising test results</li>
<li data-section-id="1auwtmq" data-start="14290" data-end="14328">recommending resilience improvements</li>
</ul>
<h3 data-section-id="159eq4o" data-start="14330" data-end="14348">AI should not:</h3>
<ul data-start="14350" data-end="14574">
<li data-section-id="1yk3ezw" data-start="14350" data-end="14397">run production failure tests without approval</li>
<li data-section-id="1wx28bi" data-start="14398" data-end="14440">disable critical infrastructure randomly</li>
<li data-section-id="17jygj8" data-start="14441" data-end="14493">terminate resources without a defined blast radius</li>
<li data-section-id="177lm12" data-start="14494" data-end="14547">test customer-facing systems without clear rollback</li>
<li data-section-id="14ntot3" data-start="14548" data-end="14574">bypass change management</li>
</ul>
<blockquote data-start="14576" data-end="14707">
<p data-start="14578" data-end="14707"><strong data-start="14578" data-end="14601">Practical takeaway:</strong><br data-start="14601" data-end="14604" />AI can design and analyse chaos experiments, but production execution must remain tightly controlled.</p>
</blockquote>
<hr data-start="14709" data-end="14712" />
<h2 data-section-id="swaxd9" data-start="14714" data-end="14769">6. Support Operations: Agents as L1 and L2 Operators</h2>
<p data-start="14771" data-end="14845">AI-assisted operations should not be limited to infrastructure monitoring.</p>
<p data-start="14847" data-end="15012">Support operations are a strong use case, especially for standardised workflows such as account maintenance, access requests, ticket routing, and operational checks.</p>
<p data-start="15014" data-end="15059">Maintaining user accounts is usually part of:</p>
<ul data-start="15061" data-end="15184">
<li data-section-id="1tmtefn" data-start="15061" data-end="15076">IT operations</li>
<li data-section-id="1c2etln" data-start="15077" data-end="15100">IT service management</li>
<li data-section-id="2o01k2" data-start="15101" data-end="15133">identity and access management</li>
<li data-section-id="ns4dl" data-start="15134" data-end="15163">access lifecycle management</li>
<li data-section-id="1y8aysh" data-start="15164" data-end="15184">support operations</li>
</ul>
<p data-start="15186" data-end="15324">It becomes part of AIOps or AI-assisted operations when AI helps with decision-making, workflow automation, diagnosis, or ticket handling.</p>
<h3 data-section-id="1jgy481" data-start="15326" data-end="15354">AI agents can help with:</h3>
<ul data-start="15356" data-end="15668">
<li data-section-id="1ljstm2" data-start="15356" data-end="15380">creating user accounts</li>
<li data-section-id="zknt4y" data-start="15381" data-end="15408">disabling leaver accounts</li>
<li data-section-id="13fypph" data-start="15409" data-end="15437">processing access requests</li>
<li data-section-id="10toulf" data-start="15438" data-end="15455">routing tickets</li>
<li data-section-id="12udrlr" data-start="15456" data-end="15478">validating approvals</li>
<li data-section-id="n8w4qr" data-start="15479" data-end="15506">checking group membership</li>
<li data-section-id="s84lyf" data-start="15507" data-end="15533">identifying stale access</li>
<li data-section-id="1w50vkd" data-start="15534" data-end="15571">updating Jira or ServiceNow tickets</li>
<li data-section-id="4bd3yw" data-start="15572" data-end="15599">generating audit evidence</li>
<li data-section-id="bbvqhp" data-start="15600" data-end="15638">answering standard support questions</li>
<li data-section-id="1jccawy" data-start="15639" data-end="15668">escalating unusual requests</li>
</ul>
<h3 data-section-id="pq5urf" data-start="15670" data-end="15714">Example: Safe AI-assisted access request</h3>
<p data-start="15716" data-end="15759">A controlled workflow could look like this:</p>
<ol data-start="15761" data-end="16108">
<li data-section-id="gbqtg8" data-start="15761" data-end="15795">User submits an access request.</li>
<li data-section-id="uuz982" data-start="15796" data-end="15822">Agent reads the ticket.</li>
<li data-section-id="1ehflko" data-start="15823" data-end="15881">Agent identifies the requested system and access level.</li>
<li data-section-id="f732bh" data-start="15882" data-end="15905">Agent checks policy.</li>
<li data-section-id="1ui6ob5" data-start="15906" data-end="15955">Agent checks manager or system owner approval.</li>
<li data-section-id="1be3ua6" data-start="15956" data-end="16005">Agent calls the IAM API only if policy allows.</li>
<li data-section-id="1e4fnlg" data-start="16006" data-end="16034">Agent updates the ticket.</li>
<li data-section-id="6z36do" data-start="16035" data-end="16064">Agent writes an audit log.</li>
<li data-section-id="1gsw07z" data-start="16065" data-end="16108">Agent schedules access review or expiry.</li>
</ol>
<p data-start="16110" data-end="16183">This is very different from giving an AI agent unrestricted admin access.</p>
<h3 data-section-id="orxxhg" data-start="16185" data-end="16203">Unsafe pattern</h3>
<p data-start="16205" data-end="16222">Avoid this model:</p>
<ul data-start="16224" data-end="16415">
<li data-section-id="4gaurf" data-start="16224" data-end="16251">AI has full admin rights.</li>
<li data-section-id="107sgfz" data-start="16252" data-end="16287">AI decides access without policy.</li>
<li data-section-id="7idajj" data-start="16288" data-end="16335">AI grants production access without approval.</li>
<li data-section-id="uxzfjn" data-start="16336" data-end="16376">AI deletes users without verification.</li>
<li data-section-id="1ao0uyk" data-start="16377" data-end="16415">AI makes changes without audit logs.</li>
</ul>
<blockquote data-start="16417" data-end="16580">
<p data-start="16419" data-end="16580"><strong data-start="16419" data-end="16442">Practical takeaway:</strong><br data-start="16442" data-end="16445" />AI can be a strong first-line operations assistant, but identity-related actions require least privilege, approval, and auditability.</p>
</blockquote>
<hr data-start="16582" data-end="16585" />
<h2 data-section-id="pqirlh" data-start="16587" data-end="16625">7. The New Role of DevOps Engineers</h2>
<p data-start="16627" data-end="16675">AI changes the work profile of DevOps engineers.</p>
<p data-start="16677" data-end="16782">The role becomes less about repetitive manual execution and more about designing safe automation systems.</p>
<div class="TyagGW_tableContainer">
<div class="group TyagGW_tableWrapper flex flex-col-reverse w-fit" tabindex="-1">
<table class="w-fit min-w-(--thread-content-width)" data-start="16784" data-end="17408">
<thead data-start="16784" data-end="16837">
<tr data-start="16784" data-end="16837">
<th class="last:pe-10" data-start="16784" data-end="16810" data-col-size="sm">Traditional DevOps Work</th>
<th class="last:pe-10" data-start="16810" data-end="16837" data-col-size="md">AI-Assisted Future Work</th>
</tr>
</thead>
<tbody data-start="16848" data-end="17408">
<tr data-start="16848" data-end="16909">
<td data-start="16848" data-end="16873" data-col-size="sm">Write scripts manually</td>
<td data-start="16873" data-end="16909" data-col-size="md">Design safe automation workflows</td>
</tr>
<tr data-start="16910" data-end="16972">
<td data-start="16910" data-end="16935" data-col-size="sm">Maintain pipeline YAML</td>
<td data-start="16935" data-end="16972" data-col-size="md">Build reusable delivery platforms</td>
</tr>
<tr data-start="16973" data-end="17040">
<td data-start="16973" data-end="17003" data-col-size="sm">Investigate alerts manually</td>
<td data-start="17003" data-end="17040" data-col-size="md">Improve telemetry and correlation</td>
</tr>
<tr data-start="17041" data-end="17099">
<td data-start="17041" data-end="17066" data-col-size="sm">Process access tickets</td>
<td data-start="17066" data-end="17099" data-col-size="md">Design governed IAM workflows</td>
</tr>
<tr data-start="17100" data-end="17170">
<td data-start="17100" data-end="17130" data-col-size="sm">Patch dependencies manually</td>
<td data-start="17130" data-end="17170" data-col-size="md">Review automated patch pull requests</td>
</tr>
<tr data-start="17171" data-end="17236">
<td data-start="17171" data-end="17196" data-col-size="sm">Collect audit evidence</td>
<td data-start="17196" data-end="17236" data-col-size="md">Build continuous compliance evidence</td>
</tr>
<tr data-start="17237" data-end="17287">
<td data-start="17237" data-end="17256" data-col-size="sm">Restart services</td>
<td data-start="17256" data-end="17287" data-col-size="md">Design self-healing systems</td>
</tr>
<tr data-start="17288" data-end="17341">
<td data-start="17288" data-end="17313" data-col-size="sm">Troubleshoot from logs</td>
<td data-start="17313" data-end="17341" data-col-size="md">Build observable systems</td>
</tr>
<tr data-start="17342" data-end="17408">
<td data-start="17342" data-end="17362" data-col-size="sm">Maintain runbooks</td>
<td data-start="17362" data-end="17408" data-col-size="md">Convert runbooks into executable workflows</td>
</tr>
</tbody>
</table>
</div>
</div>
<h3 data-section-id="rre88t" data-start="17410" data-end="17447">Skills that become more important</h3>
<p data-start="17449" data-end="17495">DevOps engineers will need stronger skills in:</p>
<ul data-start="17497" data-end="17745">
<li data-section-id="9qijv2" data-start="17497" data-end="17519">platform engineering</li>
<li data-section-id="1o23ejy" data-start="17520" data-end="17537">API integration</li>
<li data-section-id="1cf0hkn" data-start="17538" data-end="17559">MCP and tool design</li>
<li data-section-id="1wgqf3r" data-start="17560" data-end="17576">policy-as-code</li>
<li data-section-id="braw1" data-start="17577" data-end="17598">security automation</li>
<li data-section-id="1pb3jn4" data-start="17599" data-end="17620">identity governance</li>
<li data-section-id="n4za0c" data-start="17621" data-end="17648">observability engineering</li>
<li data-section-id="aswdqc" data-start="17649" data-end="17664">SRE practices</li>
<li data-section-id="8l4jx5" data-start="17665" data-end="17686">AI agent guardrails</li>
<li data-section-id="1hnfpsw" data-start="17687" data-end="17711">workflow orchestration</li>
<li data-section-id="14581fw" data-start="17712" data-end="17745">audit and compliance automation</li>
</ul>
<p data-start="17747" data-end="17923">The title may still be DevOps engineer, platform engineer, SRE, cloud engineer, or infrastructure engineer. The direction is similar: less manual operation, more system design.</p>
<hr data-start="17925" data-end="17928" />
<h2 data-section-id="11x3laz" data-start="17930" data-end="17958">8. What AI Should Not Own</h2>
<p data-start="17960" data-end="18026">AI agents should not independently control every operational task.</p>
<p data-start="18028" data-end="18116">Some actions are too risky without human approval, strong policy, and rollback controls.</p>
<h3 data-section-id="8zn4ma" data-start="18118" data-end="18137">High-risk areas</h3>
<p data-start="18139" data-end="18175">AI should not independently perform:</p>
<ul data-start="18177" data-end="18494">
<li data-section-id="1yogobm" data-start="18177" data-end="18208">production database migration</li>
<li data-section-id="vjgrjq" data-start="18209" data-end="18245">destructive infrastructure changes</li>
<li data-section-id="16nhgl3" data-start="18246" data-end="18272">IAM privilege escalation</li>
<li data-section-id="1v980il" data-start="18273" data-end="18296">firewall rule changes</li>
<li data-section-id="1shqjyh" data-start="18297" data-end="18325">production secret rotation</li>
<li data-section-id="ij9fsj" data-start="18326" data-end="18367">emergency rollback with customer impact</li>
<li data-section-id="11hk6al" data-start="18368" data-end="18397">deletion of cloud resources</li>
<li data-section-id="amvzdx" data-start="18398" data-end="18429">compliance exception approval</li>
<li data-section-id="igf0f6" data-start="18430" data-end="18460">financial or billing changes</li>
<li data-section-id="f6e7ag" data-start="18461" data-end="18494">chaos experiments in production</li>
</ul>
<h3 data-section-id="55z6l3" data-start="18496" data-end="18521">Safer operating model</h3>
<p data-start="18523" data-end="18527">Use:</p>
<ul data-start="18529" data-end="18747">
<li data-section-id="1x7kt4n" data-start="18529" data-end="18558">read-only access by default</li>
<li data-section-id="11z33d2" data-start="18559" data-end="18584">scoped tool permissions</li>
<li data-section-id="ii7fgr" data-start="18585" data-end="18601">approval gates</li>
<li data-section-id="1wgqf3r" data-start="18602" data-end="18618">policy-as-code</li>
<li data-section-id="abpnwx" data-start="18619" data-end="18635">change windows</li>
<li data-section-id="4dfk6q" data-start="18636" data-end="18648">audit logs</li>
<li data-section-id="lu8zvk" data-start="18649" data-end="18663">dry-run mode</li>
<li data-section-id="qmeka7" data-start="18664" data-end="18687">pull request workflow</li>
<li data-section-id="eyac8r" data-start="18688" data-end="18709">break-glass process</li>
<li data-section-id="z4nrj4" data-start="18710" data-end="18747">human review for production changes</li>
</ul>
<blockquote data-start="18749" data-end="18900">
<p data-start="18751" data-end="18900"><strong data-start="18751" data-end="18760">Note:</strong><br data-start="18760" data-end="18763" />The safest first version of an AI DevOps agent is usually read-only: it observes, explains, summarises, recommends, and drafts changes.</p>
</blockquote>
<hr data-start="18902" data-end="18905" />
<h2 data-section-id="8b2oj9" data-start="18907" data-end="18954">Decision Framework: What Should AI Automate?</h2>
<div class="TyagGW_tableContainer">
<div class="group TyagGW_tableWrapper flex flex-col-reverse w-fit" tabindex="-1">
<table class="w-fit min-w-(--thread-content-width)" data-start="18956" data-end="19915">
<thead data-start="18956" data-end="19012">
<tr data-start="18956" data-end="19012">
<th class="last:pe-10" data-start="18956" data-end="18963" data-col-size="sm">Task</th>
<th class="last:pe-10" data-start="18963" data-end="18978" data-col-size="sm">Good for AI?</th>
<th class="last:pe-10" data-start="18978" data-end="19003" data-col-size="sm">Human Approval Needed?</th>
<th class="last:pe-10" data-start="19003" data-end="19012" data-col-size="sm">Notes</th>
</tr>
</thead>
<tbody data-start="19033" data-end="19915">
<tr data-start="19033" data-end="19081">
<td data-start="19033" data-end="19058" data-col-size="sm">Summarise failed build</td>
<td data-start="19058" data-end="19064" data-col-size="sm">Yes</td>
<td data-start="19064" data-end="19069" data-col-size="sm">No</td>
<td data-start="19069" data-end="19081" data-col-size="sm">Low risk</td>
</tr>
<tr data-start="19082" data-end="19154">
<td data-start="19082" data-end="19104" data-col-size="sm">Generate CI/CD YAML</td>
<td data-start="19104" data-end="19110" data-col-size="sm">Yes</td>
<td data-col-size="sm" data-start="19110" data-end="19131">Review recommended</td>
<td data-col-size="sm" data-start="19131" data-end="19154">Review before merge</td>
</tr>
<tr data-start="19155" data-end="19220">
<td data-start="19155" data-end="19180" data-col-size="sm">Explain Terraform plan</td>
<td data-start="19180" data-end="19186" data-col-size="sm">Yes</td>
<td data-start="19186" data-end="19191" data-col-size="sm">No</td>
<td data-start="19191" data-end="19220" data-col-size="sm">Strong assistant use case</td>
</tr>
<tr data-start="19221" data-end="19297">
<td data-start="19221" data-end="19246" data-col-size="sm">Apply Terraform to dev</td>
<td data-start="19246" data-end="19258" data-col-size="sm">Sometimes</td>
<td data-start="19258" data-end="19268" data-col-size="sm">Depends</td>
<td data-start="19268" data-end="19297" data-col-size="sm">Safe only with guardrails</td>
</tr>
<tr data-start="19298" data-end="19359">
<td data-start="19298" data-end="19330" data-col-size="sm">Apply Terraform to production</td>
<td data-start="19330" data-end="19340" data-col-size="sm">Limited</td>
<td data-start="19340" data-end="19346" data-col-size="sm">Yes</td>
<td data-start="19346" data-end="19359" data-col-size="sm">High risk</td>
</tr>
<tr data-start="19360" data-end="19405">
<td data-start="19360" data-end="19375" data-col-size="sm">Detect drift</td>
<td data-col-size="sm" data-start="19375" data-end="19381">Yes</td>
<td data-col-size="sm" data-start="19381" data-end="19386">No</td>
<td data-col-size="sm" data-start="19386" data-end="19405">Strong use case</td>
</tr>
<tr data-start="19406" data-end="19466">
<td data-start="19406" data-end="19432" data-col-size="sm">Fix drift automatically</td>
<td data-start="19432" data-end="19444" data-col-size="sm">Sometimes</td>
<td data-start="19444" data-end="19450" data-col-size="sm">Yes</td>
<td data-start="19450" data-end="19466" data-col-size="sm">Needs review</td>
</tr>
<tr data-start="19467" data-end="19518">
<td data-start="19467" data-end="19484" data-col-size="sm">Analyse alerts</td>
<td data-start="19484" data-end="19490" data-col-size="sm">Yes</td>
<td data-start="19490" data-end="19495" data-col-size="sm">No</td>
<td data-start="19495" data-end="19518" data-col-size="sm">Good AIOps use case</td>
</tr>
<tr data-start="19519" data-end="19591">
<td data-start="19519" data-end="19537" data-col-size="sm">Restart service</td>
<td data-start="19537" data-end="19549" data-col-size="sm">Sometimes</td>
<td data-start="19549" data-end="19559" data-col-size="sm">Depends</td>
<td data-start="19559" data-end="19591" data-col-size="sm">Safer for stateless services</td>
</tr>
<tr data-start="19592" data-end="19671">
<td data-start="19592" data-end="19612" data-col-size="sm">Grant user access</td>
<td data-start="19612" data-end="19624" data-col-size="sm">Sometimes</td>
<td data-start="19624" data-end="19652" data-col-size="sm">Yes for sensitive systems</td>
<td data-start="19652" data-end="19671" data-col-size="sm">Requires policy</td>
</tr>
<tr data-start="19672" data-end="19746">
<td data-start="19672" data-end="19695" data-col-size="sm">Revoke leaver access</td>
<td data-start="19695" data-end="19701" data-col-size="sm">Yes</td>
<td data-start="19701" data-end="19725" data-col-size="sm">Often workflow-driven</td>
<td data-start="19725" data-end="19746" data-col-size="sm">Should be audited</td>
</tr>
<tr data-start="19747" data-end="19813">
<td data-start="19747" data-end="19766" data-col-size="sm">Patch dependency</td>
<td data-start="19766" data-end="19772" data-col-size="sm">Yes</td>
<td data-start="19772" data-end="19796" data-col-size="sm">Yes before production</td>
<td data-start="19796" data-end="19813" data-col-size="sm">Needs testing</td>
</tr>
<tr data-start="19814" data-end="19853">
<td data-start="19814" data-end="19830" data-col-size="sm">Update ticket</td>
<td data-col-size="sm" data-start="19830" data-end="19836">Yes</td>
<td data-col-size="sm" data-start="19836" data-end="19841">No</td>
<td data-col-size="sm" data-start="19841" data-end="19853">Low risk</td>
</tr>
<tr data-start="19854" data-end="19915">
<td data-start="19854" data-end="19877" data-col-size="sm">Run chaos experiment</td>
<td data-start="19877" data-end="19887" data-col-size="sm">Limited</td>
<td data-start="19887" data-end="19893" data-col-size="sm">Yes</td>
<td data-start="19893" data-end="19915" data-col-size="sm">Needs strict scope</td>
</tr>
</tbody>
</table>
<h2>Architecture Pattern: Governed AI DevOps Agent</h2>
<p><img data-recalc-dims="1" fetchpriority="high" decoding="async" class="alignnone wp-image-1840 size-large" src="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/05/mermaid-diagram.png?resize=678%2C520&#038;ssl=1" alt="Diagram showing an AI DevOps agent connected to delivery, infrastructure, monitoring, security, IAM, and ticketing systems through governed tool access." width="678" height="520" srcset="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/05/mermaid-diagram-scaled.png?resize=1024%2C786&amp;ssl=1 1024w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/05/mermaid-diagram-scaled.png?resize=300%2C230&amp;ssl=1 300w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/05/mermaid-diagram-scaled.png?resize=768%2C590&amp;ssl=1 768w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/05/mermaid-diagram-scaled.png?resize=1536%2C1179&amp;ssl=1 1536w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/05/mermaid-diagram-scaled.png?resize=2048%2C1572&amp;ssl=1 2048w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/05/mermaid-diagram-scaled.png?resize=80%2C60&amp;ssl=1 80w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/05/mermaid-diagram-scaled.png?w=1356&amp;ssl=1 1356w" sizes="(max-width: 678px) 100vw, 678px" /></p>
<h3 data-section-id="1usuj5b" data-start="20719" data-end="20746">What this diagram shows</h3>
<ul data-start="20748" data-end="20970">
<li data-section-id="13ei7x9" data-start="20748" data-end="20794">The AI agent is not the source of authority.</li>
<li data-section-id="10os1ul" data-start="20795" data-end="20839">It reads context from operational systems.</li>
<li data-section-id="afn76z" data-start="20840" data-end="20865">It uses approved tools.</li>
<li data-section-id="byv7wd" data-start="20866" data-end="20906">Policies decide what can be automated.</li>
<li data-section-id="55ut3t" data-start="20907" data-end="20944">High-risk changes require approval.</li>
<li data-section-id="169rm59" data-start="20945" data-end="20970">Every action is logged.</li>
</ul>
<hr data-start="20972" data-end="20975" />
<h2 data-section-id="10qn8xj" data-start="20977" data-end="20995">Common Mistakes</h2>
<h3 data-section-id="xmawb6" data-start="20997" data-end="21051">Mistake 1: Treating AI as a replacement for DevOps</h3>
<p data-start="21053" data-end="21123">AI is an assistant and automation layer. It does not remove ownership.</p>
<h3 data-section-id="81ikm1" data-start="21125" data-end="21172">Mistake 2: Giving the agent too much access</h3>
<p data-start="21174" data-end="21273">Over-permissioned agents create serious operational and security risk. Start with read-only access.</p>
<h3 data-section-id="dea5q5" data-start="21275" data-end="21324">Mistake 3: Replacing IaC state with AI memory</h3>
<p data-start="21326" data-end="21414">AI memory is not infrastructure state. Use AI to improve IaC workflows, not bypass them.</p>
<h3 data-section-id="1p30csi" data-start="21416" data-end="21463">Mistake 4: Automating without observability</h3>
<p data-start="21465" data-end="21534">AI needs reliable signals. Bad telemetry creates bad recommendations.</p>
<h3 data-section-id="1e2a74b" data-start="21536" data-end="21565">Mistake 5: No audit trail</h3>
<p data-start="21567" data-end="21659">Every agent action should be logged, especially for production, security, and IAM workflows.</p>
<h3 data-section-id="1pehnpf" data-start="21661" data-end="21694">Mistake 6: No rollback design</h3>
<p data-start="21696" data-end="21748">Automation without rollback increases incident risk.</p>
<h3 data-section-id="12lbzem" data-start="21750" data-end="21783">Mistake 7: No policy boundary</h3>
<p data-start="21785" data-end="21809">Agents need clear rules:</p>
<ul data-start="21811" data-end="21904">
<li data-section-id="1tec0ws" data-start="21811" data-end="21831">what they can read</li>
<li data-section-id="1fez1m2" data-start="21832" data-end="21855">what they can suggest</li>
<li data-section-id="ro0dv5" data-start="21856" data-end="21879">what they can execute</li>
<li data-section-id="2axbln" data-start="21880" data-end="21904">what requires approval</li>
</ul>
<hr data-start="21906" data-end="21909" />
<h2 data-section-id="1a9f7fx" data-start="21911" data-end="21928">Best Practices</h2>
<h3 data-section-id="3qsysr" data-start="21930" data-end="21964">Start with read-only use cases</h3>
<p data-start="21966" data-end="21995">Good first use cases include:</p>
<ul data-start="21997" data-end="22184">
<li data-section-id="1hbpaqq" data-start="21997" data-end="22020">summarizing incidents</li>
<li data-section-id="es6i7e" data-start="22021" data-end="22048">explaining build failures</li>
<li data-section-id="1n4zxoi" data-start="22049" data-end="22066">detecting drift</li>
<li data-section-id="1ydfqlz" data-start="22067" data-end="22083">analyzing logs</li>
<li data-section-id="qthdjp" data-start="22084" data-end="22109">reviewing pull requests</li>
<li data-section-id="1txtm58" data-start="22110" data-end="22136">checking vulnerabilities</li>
<li data-section-id="1qql4zv" data-start="22137" data-end="22155">updating tickets</li>
<li data-section-id="1mpz2u9" data-start="22156" data-end="22184">generating audit summaries</li>
</ul>
<h3 data-section-id="7us2p5" data-start="22186" data-end="22217">Move to low-risk automation</h3>
<p data-start="22219" data-end="22271">After the team gains confidence, allow the agent to:</p>
<ul data-start="22273" data-end="22466">
<li data-section-id="346wx7" data-start="22273" data-end="22294">create Jira tickets</li>
<li data-section-id="40ahwp" data-start="22295" data-end="22319">generate release notes</li>
<li data-section-id="14b36dh" data-start="22320" data-end="22358">open dependency update pull requests</li>
<li data-section-id="1uxz0x0" data-start="22359" data-end="22382">notify service owners</li>
<li data-section-id="13r6t6" data-start="22383" data-end="22406">create draft runbooks</li>
<li data-section-id="1x4uudz" data-start="22407" data-end="22435">prepare incident timelines</li>
<li data-section-id="1a997xh" data-start="22436" data-end="22466">produce compliance summaries</li>
</ul>
<h3 data-section-id="1uog7v5" data-start="22468" data-end="22502">Add controlled execution later</h3>
<p data-start="22504" data-end="22563">Only mature teams should allow execution workflows such as:</p>
<ul data-start="22565" data-end="22722">
<li data-section-id="143e12w" data-start="22565" data-end="22599">restarting non-critical services</li>
<li data-section-id="xm2wn3" data-start="22600" data-end="22630">provisioning low-risk access</li>
<li data-section-id="1hd8qzz" data-start="22631" data-end="22673">applying development environment changes</li>
<li data-section-id="1r9p4hi" data-start="22674" data-end="22722">rolling back failed non-production deployments</li>
</ul>
<h3 data-section-id="h32y4a" data-start="22724" data-end="22746">Use policy-as-code</h3>
<p data-start="22748" data-end="22840">Define what the agent can and cannot do. Keep those rules version-controlled and reviewable.</p>
<h3 data-section-id="1sum4r9" data-start="22842" data-end="22869">Keep humans responsible</h3>
<p data-start="22871" data-end="22943">AI can recommend. AI can automate. Humans still own production outcomes.</p>
<hr data-start="22945" data-end="22948" />
<h2 data-section-id="8dtpi" data-start="22950" data-end="22963">Conclusion</h2>
<p data-start="22965" data-end="22989">AI will not kill DevOps.</p>
<p data-start="22991" data-end="23208">It will remove many repetitive DevOps tasks. It will make weak practices more visible. It will increase demand for platform engineering, SRE, DevSecOps, identity governance, observability, and automation architecture.</p>
<p data-start="23210" data-end="23276">DevOps engineers who only operate tools manually may be disrupted.</p>
<p data-start="23278" data-end="23392">DevOps engineers who design safe, reliable, observable, and governed automation systems will become more valuable.</p>
<p data-start="23394" data-end="23424">The future is not “no DevOps.”</p>
<p data-start="23426" data-end="23440">The future is:</p>
<blockquote data-start="23442" data-end="23505">
<p data-start="23444" data-end="23505"><strong data-start="23444" data-end="23505">AI-assisted, policy-governed, platform-driven operations.</strong></p>
</blockquote>
</div>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.chunho-ling.com/ai-vs-devops-automate-smarter-safer/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1839</post-id>	</item>
		<item>
		<title>[AI] Stateful Knowledge Management: Implementing the LLM-Wiki Architecture</title>
		<link>https://www.chunho-ling.com/ai-stateful-knowledge-management-implementing-the-llm-wiki-architecture/</link>
					<comments>https://www.chunho-ling.com/ai-stateful-knowledge-management-implementing-the-llm-wiki-architecture/#respond</comments>
		
		<dc:creator><![CDATA[C.H. Ling]]></dc:creator>
		<pubDate>Sun, 19 Apr 2026 16:55:04 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[Programming]]></category>
		<guid isPermaLink="false">https://www.chunho-ling.com/?p=1831</guid>

					<description><![CDATA[Standard Retrieval-Augmented Generation (RAG) processes execute static retrieval at query time, forcing the model to rediscover connections across disparate document fragments without compounding knowledge [1]. The LLM-Wiki paradigm delegates the maintenance of a structured, interlinked <a class="mh-excerpt-more" href="https://www.chunho-ling.com/ai-stateful-knowledge-management-implementing-the-llm-wiki-architecture/" title="[AI] Stateful Knowledge Management: Implementing the LLM-Wiki Architecture">[...]</a>]]></description>
										<content:encoded><![CDATA[<p data-path-to-node="2">Standard Retrieval-Augmented Generation (RAG) processes execute static retrieval at query time, forcing the model to rediscover connections across disparate document fragments without compounding knowledge [1]. The LLM-Wiki paradigm delegates the maintenance of a structured, interlinked knowledge base directly to the language model.</p>
<blockquote data-path-to-node="3">
<p data-path-to-node="3,0">&#8220;The knowledge is compiled once and then kept current, not re-derived on every query.&#8221; [1]</p>
</blockquote>
<p data-path-to-node="4">The model operates as an autonomous compiler, extracting entities from new sources and persistently updating cross-references, contradictions, and topic summaries within a centralized repository [1].<span id="more-1831"></span></p>
<h2 data-path-to-node="5">1. System Layers and Directory Structure</h2>
<p data-path-to-node="6">The architecture enforces strict separation of concerns across three system layers [1]:</p>
<table data-path-to-node="7">
<thead>
<tr>
<td><strong>Layer</strong></td>
<td><strong>Functional Scope</strong></td>
<td><strong>Target Data State</strong></td>
</tr>
</thead>
<tbody>
<tr>
<td><span data-path-to-node="7,1,0,0"><b data-path-to-node="7,1,0,0" data-index-in-node="0">Raw Sources</b> (<code data-path-to-node="7,1,0,0" data-index-in-node="13">raw/</code>)</span></td>
<td><span data-path-to-node="7,1,1,0">Immutable storage for original curated documents. Read-only access for the LLM.</span></td>
<td><span data-path-to-node="7,1,2,0">Unstructured documents, images, datasets.</span></td>
</tr>
<tr>
<td><span data-path-to-node="7,2,0,0"><b data-path-to-node="7,2,0,0" data-index-in-node="0">The Wiki</b> (<code data-path-to-node="7,2,0,0" data-index-in-node="10">wiki/</code>)</span></td>
<td><span data-path-to-node="7,2,1,0">Dynamically generated domain markdown files. Exclusively written and maintained by the LLM.</span></td>
<td><span data-path-to-node="7,2,2,0">Structured Markdown (<code data-path-to-node="7,2,2,0" data-index-in-node="21">.md</code>).</span></td>
</tr>
<tr>
<td><span data-path-to-node="7,3,0,0"><b data-path-to-node="7,3,0,0" data-index-in-node="0">The Schema</b> (<code data-path-to-node="7,3,0,0" data-index-in-node="12">GEMINI.md</code>)</span></td>
<td><span data-path-to-node="7,3,1,0">Operational directives, state management constraints, and LLM behavior definitions.</span></td>
<td><span data-path-to-node="7,3,2,0">System Configuration (<code data-path-to-node="7,3,2,0" data-index-in-node="22">.md</code>).</span></td>
</tr>
</tbody>
</table>
<h2 data-path-to-node="8">2. Execution Operations</h2>
<p data-path-to-node="9">The framework relies on three primary operational vectors [1]:</p>
<ul data-path-to-node="10">
<li>
<p data-path-to-node="10,0,0"><b data-path-to-node="10,0,0" data-index-in-node="0">Ingest</b>: The LLM reads raw data, generates summary pages, updates index structures, and modifies existing domain entities.</p>
</li>
<li>
<p data-path-to-node="10,1,0"><b data-path-to-node="10,1,0" data-index-in-node="0">Query</b>: The LLM reads the compiled <code data-path-to-node="10,1,0" data-index-in-node="34">index.md</code> to locate relevant pages before synthesizing referenced answers.</p>
</li>
<li>
<p data-path-to-node="10,2,0"><b data-path-to-node="10,2,0" data-index-in-node="0">Lint</b>: The LLM conducts autonomous health-checks to flag contradictions, locate orphaned pages, and update missing cross-references.</p>
</li>
</ul>
<h2 data-path-to-node="11">3. Schema Configuration: GEMINI.md</h2>
<p data-path-to-node="12">The Schema dictates the LLM&#8217;s operational logic and file system constraints. The following configuration establishes the system instructions for the compiler agent. It must be saved as <code data-path-to-node="12" data-index-in-node="185">GEMINI.md</code> in the root directory.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic"># LLM Wiki Schema

A project for managing and generating a stateful, hierarchical knowledge base using Large Language Models (LLMs).

## Directory Overview

| Directory | Core Purpose | Target File Type |
| :--- | :--- | :--- |
| `raw/` | Storage for immutable raw source materials. | Unstructured text, datasets, documents. |
| `tools/` | Automation scripts, routing, sanitization, and transformation pipelines. | Source code. |
| `wiki/` | Destination for structured knowledge, subdivided by domain. | Markdown (`.md`). |

## Future Instructions for Gemini: Compiler Agent

When interacting with this project, you operate strictly as an objective, domain-aware LLM Wiki Compiler. To prevent prompt injection and unauthorized file modifications, you are prohibited from directly writing files. All state modifications must be executed via the `tools/` directory scripts.

### Operating Inputs

You will process contextual inputs during ingestion:
1. **MASTER INDEX**: The current contents of `wiki/index.md`.
2. **TARGET DOMAIN CONTEXT**: The concatenated contents of relevant `.md` files from `wiki/[domain]/`.
3. **NEW RAW DATA**: The text of a new document from the `raw/` directory.

### Operating Parameters

* **Domain Categorization**: Assign extracted entities to the most appropriate domain subdirectory. Each key concept must have its own individual file.
* **Data Extraction &amp; Generation**: Extract verifiable facts and concepts. If knowledge is missing in the current wiki state, generate the required `wiki/` content.
* **Directory Integrity**: You possess zero direct write access to the file system. You must execute state changes exclusively through the provided routing scripts in the `tools/` directory.
* **State Preservation**: Merge new data into existing `wiki/[domain]/[Entity].md` structures. Preserve all previously verified facts.
* **Cross-Referencing**: Hyperlink entities across pages and domains using relative WikiLinks.
* **Contradiction Flagging**: If new raw data contradicts existing wiki state, append the exact string: `&gt; **CONTRADICTION FLAG**: [Explanation]`.

### Output Formatting Constraints

You must output execution commands directed at the `tools/update_wiki` script. The output must strictly follow this YAML-in-delimiter format. Use the YAML block scalar indicator (`|`) for the markdown content. 

@@@execute tools/update_wiki
operations:
  - action: modify_or_create
    target_path: wiki/[domain]/[Entity].md
    index_summary: "[One-line summary for the domain index.md]"
    markdown_content: |
      [Full Markdown content incorporating extracted facts, WikiLinks, and code snippets]
@@@</pre>
<h2 data-path-to-node="14">4. Deployment via Gemini CLI</h2>
<p data-path-to-node="15">The <code data-path-to-node="15" data-index-in-node="4">@google/gemini-cli</code> package enables direct terminal execution of the schema directives.</p>
<ul data-path-to-node="16">
<li style="list-style-type: none;">
<ul data-path-to-node="16">
<li>
<p data-path-to-node="16,0,0"><b data-path-to-node="16,0,0" data-index-in-node="0">Dependency Installation:</b></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npm install -g @google/gemini-cli</pre>
<p><b data-path-to-node="16,1,0" data-index-in-node="0">Agent Execution:</b> Run the CLI in non-interactive mode. The local <code data-path-to-node="16,1,0" data-index-in-node="64">GEMINI.md</code> schema acts as the default contextual grounding anchor.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">gemini -p "Ingest raw/network_logs_0419.txt. Update wiki if knowledge not found. Highlight different if discrepancy found."</pre>
</li>
</ul>
</li>
</ul>
<h2 data-path-to-node="17">References</h2>
<ul data-path-to-node="16">
<li style="list-style-type: none;">
<ul data-path-to-node="16">
<li style="list-style-type: none;">
<ul data-path-to-node="18">
<li>
<p data-path-to-node="18,0,0">[1] A. Karpathy. &#8220;llm-wiki.md&#8221;. GitHub Gists. <a class="ng-star-inserted" href="https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f" target="_blank" rel="noopener" data-hveid="0" data-ved="0CAAQ_4QMahgKEwi3_pHm6fmTAxUAAAAAHQAAAAAQrQw">https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f</a></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.chunho-ling.com/ai-stateful-knowledge-management-implementing-the-llm-wiki-architecture/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1831</post-id>	</item>
		<item>
		<title>MCP vs. OpenAPI: A Technical Architecture Judgment for 2026</title>
		<link>https://www.chunho-ling.com/mcp-vs-openapi-a-technical-architecture-judgment-for-2026/</link>
					<comments>https://www.chunho-ling.com/mcp-vs-openapi-a-technical-architecture-judgment-for-2026/#respond</comments>
		
		<dc:creator><![CDATA[C.H. Ling]]></dc:creator>
		<pubDate>Thu, 16 Apr 2026 12:00:53 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">https://www.chunho-ling.com/?p=1820</guid>

					<description><![CDATA[As AI agents move from &#8220;chatting&#8221; to &#8220;acting,&#8221; the industry is shifting from static Structural Contracts (OpenAPI) to dynamic Orchestration Protocols (MCP). While OpenAPI remains the gold standard for software-to-software communication, the Model Context Protocol <a class="mh-excerpt-more" href="https://www.chunho-ling.com/mcp-vs-openapi-a-technical-architecture-judgment-for-2026/" title="MCP vs. OpenAPI: A Technical Architecture Judgment for 2026">[...]</a>]]></description>
										<content:encoded><![CDATA[<p id="p-rc_0e02c10f66841d1d-66" data-path-to-node="4">As AI agents move from &#8220;chatting&#8221; to &#8220;acting,&#8221; the industry is shifting from static <b data-path-to-node="4" data-index-in-node="84">Structural Contracts</b> (OpenAPI) to dynamic <b data-path-to-node="4" data-index-in-node="126">Orchestration Protocols</b> (MCP). <span class="citation-115">While OpenAPI remains the gold standard for software-to-software communication, the </span><b data-path-to-node="4" data-index-in-node="241"><span class="citation-115">Model Context Protocol (MCP)</span></b><span class="citation-115 citation-end-115"> has emerged as the &#8220;USB-C for AI,&#8221; allowing models to discover and use tools at runtime without manual integration.</span><span id="more-1820"></span></p>
<p>&nbsp;</p>
<h2 data-path-to-node="6">What is MCP?</h2>
<p id="p-rc_0e02c10f66841d1d-67" data-path-to-node="7"><b data-path-to-node="7" data-index-in-node="0"><span class="citation-114">Model Context Protocol (MCP)</span></b><span class="citation-114 citation-end-114"> is an open-standard orchestration protocol (pioneered by Anthropic) that allows AI models to dynamically discover and navigate their environment.</span></p>
<ul data-path-to-node="8">
<li>
<p data-path-to-node="8,0,0"><b data-path-to-node="8,0,0" data-index-in-node="0">Mechanism:</b> Uses <b data-path-to-node="8,0,0" data-index-in-node="16">JSON-RPC 2.0</b> over stateful transports like <code data-path-to-node="8,0,0" data-index-in-node="59">stdio</code> or WebSockets/SSE.</p>
</li>
<li>
<p id="p-rc_0e02c10f66841d1d-68" data-path-to-node="8,1,0"><b data-path-to-node="8,1,0" data-index-in-node="0"><span class="citation-113">The &#8220;Pull&#8221; Model:</span></b><span class="citation-113"> Instead of hardcoding tools, the agent asks the MCP server: </span><i data-path-to-node="8,1,0" data-index-in-node="78"><span class="citation-113 citation-end-113">&#8220;What are your current capabilities?&#8221;</span></i></p>
</li>
<li>
<p id="p-rc_0e02c10f66841d1d-68" data-path-to-node="8,1,0"><i data-path-to-node="8,1,0" data-index-in-node="78"></i><b data-path-to-node="8,2,0" data-index-in-node="0"><span class="citation-112">Primitives:</span></b><span class="citation-112"> Exposes </span><b data-path-to-node="8,2,0" data-index-in-node="20"><span class="citation-112">Tools</span></b><span class="citation-112"> (actions), </span><b data-path-to-node="8,2,0" data-index-in-node="37"><span class="citation-112">Resources</span></b><span class="citation-112"> (data), and </span><b data-path-to-node="8,2,0" data-index-in-node="59"><span class="citation-112">Prompts</span></b><span class="citation-112 citation-end-112"> (templates).</span></p>
</li>
</ul>
<h2 data-path-to-node="9">What is OpenAPI?</h2>
<p id="p-rc_0e02c10f66841d1d-70" data-path-to-node="10"><b data-path-to-node="10" data-index-in-node="0"><span class="citation-111">OpenAPI (OAS)</span></b><span class="citation-111 citation-end-111"> is the industry-standard &#8220;Structural Contract&#8221; for defining RESTful Web APIs.</span></p>
<ul data-path-to-node="11">
<li>
<p data-path-to-node="11,0,0"><b data-path-to-node="11,0,0" data-index-in-node="0">Mechanism:</b> Stateless <b data-path-to-node="11,0,0" data-index-in-node="21">HTTP/REST</b> focused on structured data exchange between software systems.</p>
</li>
<li>
<p data-path-to-node="11,1,0"><b data-path-to-node="11,1,0" data-index-in-node="0">The &#8220;Push&#8221; Model:</b> To give an agent access, developers must &#8220;stuff&#8221; the OpenAPI definition into the system prompt or a RAG pipeline so the model &#8220;knows&#8221; the endpoints exist.</p>
</li>
</ul>
<hr data-path-to-node="12" />
<h2 data-path-to-node="13">Architectural Comparison: The Pro/Con Matrix</h2>
<h3 data-path-to-node="14"><b data-path-to-node="14" data-index-in-node="0">Why Choose MCP over OpenAPI?</b></h3>
<ul data-path-to-node="15">
<li>
<p id="p-rc_0e02c10f66841d1d-71" data-path-to-node="15,0,0"><b data-path-to-node="15,0,0" data-index-in-node="0">Dynamic Discovery:</b> Eliminate &#8220;Context Stuffing.&#8221; <span class="citation-110 citation-end-110">Agents discover tools at connection time, preventing &#8220;Specification Drift&#8221; where the model&#8217;s instructions don&#8217;t match the live API.</span></p>
</li>
<li>
<p id="p-rc_0e02c10f66841d1d-72" data-path-to-node="15,1,0"><b data-path-to-node="15,1,0" data-index-in-node="0"><span class="citation-109 citation-end-109">Reduced Integration Complexity (</span><span class="math-inline" data-math="M + N" data-index-in-node="32">M + N</span><span class="citation-108">):</span></b><span class="citation-108 citation-end-108"> In an OpenAPI world, </span><span class="math-inline" data-math="M" data-index-in-node="63">M</span><span class="citation-107 citation-end-107"> agents connecting to </span><span class="math-inline" data-math="N" data-index-in-node="86">N</span><span class="citation-106 citation-end-106"> APIs require </span><b data-path-to-node="15,1,0" data-index-in-node="101"><span class="math-inline" data-math="M \times N" data-index-in-node="101">M * N</span></b><span class="citation-105 citation-end-105"> custom integrations.</span></p>
<ul data-path-to-node="15,1,1">
<li>
<p id="p-rc_0e02c10f66841d1d-73" data-path-to-node="15,1,1,0,0"><span class="citation-104 citation-end-104">MCP standardizes the &#8220;handshake,&#8221; reducing complexity to </span><b data-path-to-node="15,1,1,0,0" data-index-in-node="57"><span class="math-inline" data-math="M + N" data-index-in-node="57">M + N</span></b><span class="citation-103 citation-end-103">.</span></p>
</li>
</ul>
</li>
<li>
<p data-path-to-node="15,2,0"><b data-path-to-node="15,2,0" data-index-in-node="0">Semantic Density:</b> MCP servers return <b data-path-to-node="15,2,0" data-index-in-node="37">summarized context</b> optimized for LLM &#8220;eyes,&#8221; rather than raw, verbose JSON/XML blobs that waste tokens and processing power.</p>
</li>
<li>
<p data-path-to-node="15,3,0"><b data-path-to-node="15,3,0" data-index-in-node="0">Near-Zero Latency:</b> Local agents (IDE extensions, CLIs) use <code data-path-to-node="15,3,0" data-index-in-node="59">stdio</code> for instantaneous communication without network overhead.</p>
</li>
</ul>
<h3 data-path-to-node="16"><b data-path-to-node="16" data-index-in-node="0">Why Stick with OpenAPI?</b></h3>
<ul data-path-to-node="17">
<li>
<p data-path-to-node="17,0,0"><b data-path-to-node="17,0,0" data-index-in-node="0">Production Maturity:</b> Highly compatible with existing Load Balancers, Caching layers, and global CDNs.</p>
</li>
<li>
<p id="p-rc_0e02c10f66841d1d-74" data-path-to-node="17,1,0"><b data-path-to-node="17,1,0" data-index-in-node="0">Security &amp; Auth:</b> Mature support for <b data-path-to-node="17,1,0" data-index-in-node="36">OAuth 2.0/OpenID Connect</b>. <span class="citation-102">MCP&#8217;s security is still evolving and currently faces higher risks of </span><b data-path-to-node="17,1,0" data-index-in-node="131"><span class="citation-102">Prompt Injection</span></b><span class="citation-102 citation-end-102"> (tricking the model into tool misuse).</span></p>
</li>
<li>
<p id="p-rc_0e02c10f66841d1d-75" data-path-to-node="17,2,0"><b data-path-to-node="17,2,0" data-index-in-node="0"><span class="citation-101">Interoperability:</span></b><span class="citation-101 citation-end-101"> OpenAPI remains the universal language for non-AI microservices and traditional frontend apps.</span></p>
</li>
</ul>
<hr data-path-to-node="18" />
<h2 data-path-to-node="19">The Architect’s Opinion: The &#8220;Semantic Gateway&#8221; Hybrid</h2>
<p data-path-to-node="20"><b data-path-to-node="20" data-index-in-node="0">Do not replace your Web APIs. Wrap them.</b></p>
<p data-path-to-node="21">The most resilient 2026 architecture uses an <b data-path-to-node="21" data-index-in-node="45">MCP Server as a Gateway</b> to existing REST/OpenAPI services.</p>
<p data-path-to-node="21"><img data-recalc-dims="1" decoding="async" class="alignnone wp-image-1825 size-large" src="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Gemini_Generated_Image_3kkc0m3kkc0m3kkc.png?resize=678%2C370&#038;ssl=1" alt="" width="678" height="370" srcset="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Gemini_Generated_Image_3kkc0m3kkc0m3kkc-scaled.png?resize=1024%2C559&amp;ssl=1 1024w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Gemini_Generated_Image_3kkc0m3kkc0m3kkc-scaled.png?resize=300%2C164&amp;ssl=1 300w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Gemini_Generated_Image_3kkc0m3kkc0m3kkc-scaled.png?resize=768%2C419&amp;ssl=1 768w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Gemini_Generated_Image_3kkc0m3kkc0m3kkc-scaled.png?resize=1536%2C838&amp;ssl=1 1536w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Gemini_Generated_Image_3kkc0m3kkc0m3kkc-scaled.png?resize=2048%2C1117&amp;ssl=1 2048w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Gemini_Generated_Image_3kkc0m3kkc0m3kkc-scaled.png?w=1356&amp;ssl=1 1356w" sizes="(max-width: 678px) 100vw, 678px" /></p>
<h3 data-path-to-node="22"><b data-path-to-node="22" data-index-in-node="0">Key Strategies for the Hybrid Model:</b></h3>
<ol start="1" data-path-to-node="23">
<li>
<p data-path-to-node="23,0,0"><b data-path-to-node="23,0,0" data-index-in-node="0">Intent-Based Mapping:</b> Do not map 1 API to 1 Tool. Instead, bundle multiple endpoints into a <b data-path-to-node="23,0,0" data-index-in-node="92">High-Level Intent</b>.</p>
<ul data-path-to-node="23,0,1">
<li>
<p data-path-to-node="23,0,1,0,0"><i data-path-to-node="23,0,1,0,0" data-index-in-node="0">Example:</i> An <code data-path-to-node="23,0,1,0,0" data-index-in-node="12">update-subscription</code> MCP tool might orchestrate a <code data-path-to-node="23,0,1,0,0" data-index-in-node="61">GET /user</code>, a <code data-path-to-node="23,0,1,0,0" data-index-in-node="74">PUT /billing</code>, and a <code data-path-to-node="23,0,1,0,0" data-index-in-node="94">POST /notify</code> behind the scenes.</p>
</li>
</ul>
</li>
<li>
<p data-path-to-node="23,1,0"><b data-path-to-node="23,1,0" data-index-in-node="0">Automated Bridging:</b> Use tools like <code data-path-to-node="23,1,0" data-index-in-node="35">openapi-to-mcp</code> to generate your base server, ensuring your OpenAPI spec remains the <b data-path-to-node="23,1,0" data-index-in-node="119">Single Source of Truth</b>.</p>
</li>
<li>
<p data-path-to-node="23,2,0"><b data-path-to-node="23,2,0" data-index-in-node="0">Hardened Security:</b></p>
<ul data-path-to-node="23,2,1">
<li>
<p id="p-rc_0e02c10f66841d1d-76" data-path-to-node="23,2,1,0,0"><b data-path-to-node="23,2,1,0,0" data-index-in-node="0"><span class="citation-100">Scoped Authentication:</span></b><span class="citation-100"> Use the Gateway to exchange the agent&#8217;s identity for </span><b data-path-to-node="23,2,1,0,0" data-index-in-node="76"><span class="citation-100">Short-lived, Down-scoped Tokens</span></b><span class="citation-100 citation-end-100">.</span></p>
</li>
<li>
<p data-path-to-node="23,2,1,1,0"><b data-path-to-node="23,2,1,1,0" data-index-in-node="0">Semantic Guardrails:</b> Implement a policy layer (like OPA or an LLM guardrail) to validate tool parameters before execution.</p>
</li>
</ul>
</li>
</ol>
<hr data-path-to-node="24" />
<h2 data-path-to-node="25">Use Case: The Evolution of &#8220;Action&#8221;</h2>
<table data-path-to-node="26">
<thead>
<tr>
<td><strong>Feature</strong></td>
<td><strong>Booking via App (OpenAPI/UI)</strong></td>
<td><strong>Booking via Gemini (MCP)</strong></td>
</tr>
</thead>
<tbody>
<tr>
<td><span data-path-to-node="26,1,0,0"><b data-path-to-node="26,1,0,0" data-index-in-node="0">Workflow</b></span></td>
<td><span data-path-to-node="26,1,1,0"><b data-path-to-node="26,1,1,0" data-index-in-node="0">Imperative:</b> User clicks filters, selects time, fills forms.</span></td>
<td><span data-path-to-node="26,1,2,0"><b data-path-to-node="26,1,2,0" data-index-in-node="0">Declarative:</b> User says &#8220;Find a table for 4 tonight.&#8221;</span></td>
</tr>
<tr>
<td><span data-path-to-node="26,2,0,0"><b data-path-to-node="26,2,0,0" data-index-in-node="0">Integration</b></span></td>
<td><span data-path-to-node="26,2,1,0">Hardcoded into the app&#8217;s frontend.</span></td>
<td><span data-path-to-node="26,2,2,0">Dynamically discovered via <code data-path-to-node="26,2,2,0" data-index-in-node="27">list_tools</code>.</span></td>
</tr>
<tr>
<td><span data-path-to-node="26,3,0,0"><b data-path-to-node="26,3,0,0" data-index-in-node="0">Orchestration</b></span></td>
<td><span data-path-to-node="26,3,1,0">User manages the steps.</span></td>
<td><span data-path-to-node="26,3,2,0">The Model decides which tools to call and in what order.</span></td>
</tr>
</tbody>
</table>
<p id="p-rc_0e02c10f66841d1d-77" data-path-to-node="27"><b data-path-to-node="27" data-index-in-node="0">Final Verdict:</b><span class="citation-99"> Use </span><b data-path-to-node="27" data-index-in-node="19"><span class="citation-99">OpenAPI</span></b><span class="citation-99"> for your data&#8217;s foundation and </span><b data-path-to-node="27" data-index-in-node="58"><span class="citation-99">MCP</span></b><span class="citation-99 citation-end-99"> as the intelligent interface that lets AI agents actually put that data to work.</span></p>
<p>&nbsp;</p>
<hr data-path-to-node="28" />
<h3 data-path-to-node="29">Recommended Architect&#8217;s Toolkit</h3>
<ul data-path-to-node="30">
<li>
<p id="p-rc_0e02c10f66841d1d-78" data-path-to-node="30,0,0"><b data-path-to-node="30,0,0" data-index-in-node="0"><code data-path-to-node="30,0,0" data-index-in-node="0"><span class="citation-98">mcp-server-openapi</span></code></b><span class="citation-98 citation-end-98">: A CLI to instantly turn your spec into an MCP server.</span></p>
</li>
<li>
<p id="p-rc_0e02c10f66841d1d-79" data-path-to-node="30,1,0"><b data-path-to-node="30,1,0" data-index-in-node="0"><span class="citation-97">OAuth 2.1</span></b><span class="citation-97 citation-end-97">: The 2026 requirement for secure, remote MCP-to-API communication.</span></p>
</li>
</ul>
<h3>Reference</h3>
<ul data-path-to-node="6">
<li>
<p id="p-rc_cd61c6d022220771-94" data-path-to-node="2,0,0"><b data-path-to-node="2,0,0" data-index-in-node="0"><span class="citation-144">Model Context Protocol (MCP) Official Docs:</span></b><span class="citation-144 citation-end-144"> &#8220;Introduction to MCP: The USB-C for AI Applications.&#8221;</span> <i data-path-to-node="2,0,0" data-index-in-node="98">Anthropic / ModelContextProtocol.io (2026).</i> <a class="ng-star-inserted" href="https://modelcontextprotocol.io/docs/getting-started/intro" target="_blank" rel="noopener" data-hveid="0" data-ved="0CAAQ_4QMahgKEwjX07ydn--TAxUAAAAAHQAAAAAQ-Ak">https://modelcontextprotocol.io/docs/getting-started/intro</a></p>
</li>
<li>
<p data-path-to-node="2,1,0"><b data-path-to-node="2,1,0" data-index-in-node="0">OpenAPI Specification (v3.1.x):</b> &#8220;The Structural Contract for RESTful Services.&#8221; <i data-path-to-node="2,1,0" data-index-in-node="80">OpenAPI Initiative.</i> <a class="ng-star-inserted" href="https://www.openapis.org/" target="_blank" rel="noopener" data-hveid="0" data-ved="0CAAQ_4QMahgKEwjX07ydn--TAxUAAAAAHQAAAAAQ-gk">https://www.openapis.org/</a></p>
</li>
<li>
<p id="p-rc_cd61c6d022220771-95" data-path-to-node="4,0,0"><b data-path-to-node="4,0,0" data-index-in-node="0">The <span class="math-inline" data-math="M+N" data-index-in-node="4">$M+N$</span> Integration Problem:</b> &#8220;Why Enterprises are Switching to MCP in 2026: Solving the <span class="math-inline" data-math="M \times N" data-index-in-node="88">$M \times N$</span> Complexity Gap.&#8221; <i data-path-to-node="4,0,0" data-index-in-node="116"><span class="citation-143">Signity Solutions (March 2024/2026).</span></i> <a class="ng-star-inserted" href="https://www.signitysolutions.com/tech-insights/mcp-vs-traditional-api-integration" target="_blank" rel="noopener" data-hveid="0" data-ved="0CAAQ_4QMahgKEwjX07ydn--TAxUAAAAAHQAAAAAQ-wk">https://www.signitysolutions.com/tech-insights/mcp-vs-traditional-api-integration</a></p>
</li>
<li>
<p id="p-rc_cd61c6d022220771-96" data-path-to-node="4,1,0"><b data-path-to-node="4,1,0" data-index-in-node="0">Orchestration vs. <span class="citation-142">Data Transport:</span></b><span class="citation-142 citation-end-142"> &#8220;MCP vs. REST API: Comparing Warehouse Management to a Forklift.&#8221;</span> <i data-path-to-node="4,1,0" data-index-in-node="100">Loginsoft (March 2026).</i> <a class="ng-star-inserted" href="https://www.loginsoft.com/post/mcp-vs-api-whats-the-actual-difference-and-when-to-use-each" target="_blank" rel="noopener" data-hveid="0" data-ved="0CAAQ_4QMahgKEwjX07ydn--TAxUAAAAAHQAAAAAQ_Qk">https://www.loginsoft.com/post/mcp-vs-api-whats-the-actual-difference-and-when-to-use-each</a></p>
</li>
<li>
<p data-path-to-node="4,2,0"><b data-path-to-node="4,2,0" data-index-in-node="0">Performance Benchmarks:</b> &#8220;Sub-second Response Times: When to stick with REST over MCP for High-Volume Data.&#8221; <i data-path-to-node="4,2,0" data-index-in-node="108">Ryze AI (April 2026).</i> <a class="ng-star-inserted" href="https://www.get-ryze.ai/blog/rest-api-vs-mcp-google-ads-claude" target="_blank" rel="noopener" data-hveid="0" data-ved="0CAAQ_4QMahgKEwjX07ydn--TAxUAAAAAHQAAAAAQ_wk">https://www.get-ryze.ai/blog/rest-api-vs-mcp-google-ads-claude</a></p>
</li>
<li>
<p data-path-to-node="6,0,0"><b data-path-to-node="6,0,0" data-index-in-node="0">The Semantic Gateway Pattern:</b> &#8220;Generating MCP Tools from OpenAPI: Best Practices for AI-Native Descriptions.&#8221; <i data-path-to-node="6,0,0" data-index-in-node="110">Speakeasy (January 2026).</i> <a class="ng-star-inserted" href="https://www.speakeasy.com/mcp/tool-design/generate-mcp-tools-from-openapi" target="_blank" rel="noopener" data-hveid="0" data-ved="0CAAQ_4QMahgKEwjX07ydn--TAxUAAAAAHQAAAAAQgAo">https://www.speakeasy.com/mcp/tool-design/generate-mcp-tools-from-openapi</a></p>
</li>
<li>
<p id="p-rc_cd61c6d022220771-97" data-path-to-node="6,1,0"><b data-path-to-node="6,1,0" data-index-in-node="0"><span class="citation-141">OpenAPI Bridge (Rust Implementation):</span></b><span class="citation-141 citation-end-141"> &#8220;Automated Transformation of OpenAPI Operations into MCP Tools.&#8221;</span> <i data-path-to-node="6,1,0" data-index-in-node="103">MCP Market (March 2026).</i> <a class="ng-star-inserted" href="https://mcpmarket.com/server/openapi-bridge-2" target="_blank" rel="noopener" data-hveid="0" data-ved="0CAAQ_4QMahgKEwjX07ydn--TAxUAAAAAHQAAAAAQgQo">https://mcpmarket.com/server/openapi-bridge-2</a></p>
</li>
<li>
<p id="p-rc_cd61c6d022220771-98" data-path-to-node="8,0,0"><b data-path-to-node="8,0,0" data-index-in-node="0"><span class="citation-140">The 2026 Security Guide:</span></b><span class="citation-140 citation-end-140"> &#8220;Hardening MCP Servers: OAuth 2.1, Scoped Tokens, and Guarding against Tool Poisoning.&#8221;</span> <i data-path-to-node="8,0,0" data-index-in-node="113">Unicrew DevSecOps (April 2026).</i> <a class="ng-star-inserted" href="https://unicrew.com/blog/secure-mcp-server-guide/" target="_blank" rel="noopener" data-hveid="0" data-ved="0CAAQ_4QMahgKEwjX07ydn--TAxUAAAAAHQAAAAAQgwo">https://unicrew.com/blog/secure-mcp-server-guide/</a></p>
</li>
<li>
<p data-path-to-node="8,1,0"><b data-path-to-node="8,1,0" data-index-in-node="0">Defensive Architecture:</b> &#8220;Zero Trust for AI Agents: Implementing Circuit Breakers in MCP Gateways.&#8221; <i data-path-to-node="8,1,0" data-index-in-node="99">InstaTunnel (March 2026).</i> <a class="ng-star-inserted" href="https://medium.com/@instatunnel/securing-mcp-servers-the-2026-guide-to-ai-tool-tunneling-aafa113b08db" target="_blank" rel="noopener" data-hveid="0" data-ved="0CAAQ_4QMahgKEwjX07ydn--TAxUAAAAAHQAAAAAQhQo">https://medium.com/@instatunnel/securing-mcp-servers-the-2026-guide-to-ai-tool-tunneling-aafa113b08db</a></p>
</li>
</ul>
<hr data-path-to-node="9" />
<blockquote data-path-to-node="10">
<p data-path-to-node="10,0"><b data-path-to-node="10,0" data-index-in-node="0">Architect&#8217;s Note:</b> For your WordPress post, I recommend hyperlinking the <b data-path-to-node="10,0" data-index-in-node="72"><span class="math-inline" data-math="M+N" data-index-in-node="72">$M+N$</span> Complexity</b> and <b data-path-to-node="10,0" data-index-in-node="91">Semantic Gateway</b> sections specifically to the Loginsoft and Speakeasy articles, as they provide the strongest visual analogies for your readers.</p>
</blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://www.chunho-ling.com/mcp-vs-openapi-a-technical-architecture-judgment-for-2026/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1820</post-id>	</item>
		<item>
		<title>[Architectural Analysis] Multi-Agent Design Patterns in C# LLM Workflows</title>
		<link>https://www.chunho-ling.com/architectural-analysis-multi-agent-design-patterns-in-c-llm-workflows/</link>
					<comments>https://www.chunho-ling.com/architectural-analysis-multi-agent-design-patterns-in-c-llm-workflows/#respond</comments>
		
		<dc:creator><![CDATA[C.H. Ling]]></dc:creator>
		<pubDate>Wed, 15 Apr 2026 07:59:38 +0000</pubDate>
				<category><![CDATA[.net Core]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Computing]]></category>
		<category><![CDATA[Programming]]></category>
		<guid isPermaLink="false">https://www.chunho-ling.com/?p=1816</guid>

					<description><![CDATA[This article provides a complete technical analysis and code walkthrough of a C# Large Language Model (LLM) multi-agent workflow utilizing LangChain.Providers. It deconstructs core agentic implementation architectures—starting with the foundational state contract and scaling up <a class="mh-excerpt-more" href="https://www.chunho-ling.com/architectural-analysis-multi-agent-design-patterns-in-c-llm-workflows/" title="[Architectural Analysis] Multi-Agent Design Patterns in C# LLM Workflows">[...]</a>]]></description>
										<content:encoded><![CDATA[<p data-path-to-node="11">This article provides a complete technical analysis and code walkthrough of a C# Large Language Model (LLM) multi-agent workflow utilizing <code data-path-to-node="11" data-index-in-node="140">LangChain.Providers</code>. It deconstructs core agentic implementation architectures—starting with the foundational state contract and scaling up to Hub-and-Spoke routing and Generator-Evaluator loops.<span id="more-1816"></span></p>
<h2 data-path-to-node="13">1. The State Contract (Shared Memory)</h2>
<p data-path-to-node="14">In a multi-agent system, LLM API calls are inherently stateless. To maintain context across multiple agent interactions, we must establish a rigorous data contract. The <code data-path-to-node="14" data-index-in-node="169">AgentState</code> class acts as the single source of truth, passed sequentially from node to node.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="csharp">using LangChain.Providers;
using LangChain.Providers.OpenAI;
using System;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace OllamaAgenticWorkflow
{
    // ==========================================
    // STATE CONTRACT
    // ==========================================
    public class AgentState
    {
        [JsonPropertyName("original_request")]
        public string OriginalRequest { get; set; } = string.Empty;

        [JsonPropertyName("orchestrator_route")]
        public string OrchestratorRoute { get; set; } = string.Empty;

        [JsonPropertyName("generated_content")]
        public string GeneratedContent { get; set; } = string.Empty;

        [JsonPropertyName("evaluator_feedback")]
        public string EvaluatorFeedback { get; set; } = string.Empty;

        [JsonPropertyName("is_approved")]
        public bool IsApproved { get; set; } = false;

        [JsonPropertyName("iteration_count")]
        public int IterationCount { get; set; } = 0;

        public string ToJson()
        {
            var options = new JsonSerializerOptions
            {
                WriteIndented = true,
                Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping // Ensures Unicode/Chinese chars aren't escaped
            };
            return JsonSerializer.Serialize(this, options);
        }
    }</pre>
<h2 data-path-to-node="16">2. The Agent Blueprint (Template Method Pattern)</h2>
<p data-path-to-node="17">An &#8220;agent&#8221; is essentially an LLM wrapped in state management and output parsing. The <code data-path-to-node="17" data-index-in-node="85">AgentNodeBase</code> uses the <b data-path-to-node="17" data-index-in-node="108">Template Method Pattern</b> to define the standard execution sequence. It handles the API call and robust JSON extraction, forcing concrete subclasses to strictly define their Persona (System Prompt) and State Mutations.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="csharp">// ==========================================
    // ABSTRACT BASE NODE (TEMPLATE METHOD PATTERN)
    // ==========================================
    public abstract class AgentNodeBase
    {
        protected readonly IChatModel _chatModel;

        protected AgentNodeBase(IChatModel chatModel)
        {
            _chatModel = chatModel;
        }

        // Abstract methods that each specific agent must implement
        protected abstract string NodeName { get; }
        protected abstract string GetSystemPrompt();
        protected abstract string GetHumanPrompt(AgentState state);
        protected abstract void ParseAndUpdateState(AgentState state, JsonElement jsonRoot);

        // The Template Method that runs the shared execution logic
        public async Task&lt;AgentState&gt; ExecuteAsync(AgentState state)
        {
            string iterationLog = NodeName == "Generator" ? $" Attempt {state.IterationCount + 1}..." : "...";
            Console.WriteLine($"\n[{NodeName}]{iterationLog}");

            var systemPrompt = GetSystemPrompt();
            var humanPrompt = GetHumanPrompt(state);

            var response = await _chatModel.GenerateAsync(new[]
            {
                new Message(systemPrompt, MessageRole.System),
                new Message(humanPrompt, MessageRole.Human)
            });

            var cleanJson = ExtractJsonFromLlmResponse(response.LastMessageContent ?? string.Empty);
            
            // Failsafe for models that completely fail to return JSON
            if (string.IsNullOrWhiteSpace(cleanJson) || (!cleanJson.StartsWith("{") &amp;&amp; !cleanJson.StartsWith("[")))
            {
                throw new FormatException($"The LLM did not return valid JSON. Raw output: {response.LastMessageContent}");
            }

            using var jsonDoc = JsonDocument.Parse(cleanJson);
            ParseAndUpdateState(state, jsonDoc.RootElement);

            return state;
        }

        // Shared utility to strip markdown backticks from LLM responses
        private string ExtractJsonFromLlmResponse(string text)
        {
            var start = text.IndexOf('{');
            var end = text.LastIndexOf('}');
            if (start != -1 &amp;&amp; end != -1 &amp;&amp; end &gt; start)
            {
                return text.Substring(start, end - start + 1);
            }
            return text; 
        }
    }</pre>
<h2 data-path-to-node="19">3. The Worker Nodes (Generator, Evaluator, Translator)</h2>
<p data-path-to-node="20">These concrete classes inherit the blueprint.</p>
<p data-path-to-node="21">The <b data-path-to-node="21" data-index-in-node="4">Generator-Evaluator Loop</b> is visible here. The Generator produces content. If the Evaluator rejects it and provides feedback, the Orchestrator will route it back to the Generator. Notice how the <code data-path-to-node="21" data-index-in-node="198">GeneratorNode</code> specifically injects <code data-path-to-node="21" data-index-in-node="233">state.EvaluatorFeedback</code> into its next prompt to force a revision, and then explicitly wipes the feedback state so the process can restart cleanly.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="csharp">// ==========================================
    // CONCRETE AGENT NODES
    // ==========================================
    public class GeneratorNode : AgentNodeBase
    {
        public GeneratorNode(IChatModel chatModel) : base(chatModel) { }

        protected override string NodeName =&gt; "Generator";

        protected override string GetSystemPrompt() =&gt; @"
            You are a Generator Agent. Execute the task provided by the user.
            
            Respond strictly in valid JSON format:
            { ""generated_result"": ""your content here"" }";

        protected override string GetHumanPrompt(AgentState state)
        {
            // Grab the feedback BEFORE we wipe it out
            var feedbackContext = !string.IsNullOrWhiteSpace(state.EvaluatorFeedback)
                ? $"\nPREVIOUS FEEDBACK TO FIX: {state.EvaluatorFeedback}"
                : "";

            return $"Task: {state.OriginalRequest}{feedbackContext}";
        }

        protected override void ParseAndUpdateState(AgentState state, JsonElement jsonRoot)
        {
            state.GeneratedContent = jsonRoot.GetProperty("generated_result").GetString() ?? "";
            state.IterationCount++;

            state.EvaluatorFeedback = "";
            state.IsApproved = false;

            Console.WriteLine($"[{NodeName} Content]:\n{state.GeneratedContent}");
        }
    }

    public class EvaluatorNode : AgentNodeBase
    {
        public EvaluatorNode(IChatModel chatModel) : base(chatModel) { }

        protected override string NodeName =&gt; "Evaluator";

        protected override string GetSystemPrompt() =&gt; @"
            You are an Evaluator Agent. 
            Review the original task and the generated content. Does the content satisfy the task perfectly? 
            
            Respond strictly in valid JSON format using a boolean:
            { ""is_approved"": true, ""feedback"": ""reasoning"" }";

        protected override string GetHumanPrompt(AgentState state) =&gt; $@"
            Original Task: {state.OriginalRequest}
            Generated Content: {state.GeneratedContent}";

        protected override void ParseAndUpdateState(AgentState state, JsonElement jsonRoot)
        {
            state.IsApproved = jsonRoot.GetProperty("is_approved").GetBoolean();
            state.EvaluatorFeedback = jsonRoot.GetProperty("feedback").GetString() ?? "";
            Console.WriteLine($"[{NodeName} Approved?]: {state.IsApproved}");
        }
    }

    public class TranslatorNode : AgentNodeBase
    {
        public TranslatorNode(IChatModel chatModel) : base(chatModel) { }

        protected override string NodeName =&gt; "Translator";

        protected override string GetSystemPrompt() =&gt; @"
            You are a Translator Agent. Translate the provided text accurately.
            
            Respond strictly in valid JSON format. Provide the translated text:
            { ""generated_result"": ""translated text here"" }";

        protected override string GetHumanPrompt(AgentState state) =&gt; 
            $"Task: {state.OriginalRequest}";

        protected override void ParseAndUpdateState(AgentState state, JsonElement jsonRoot)
        {
            state.GeneratedContent = jsonRoot.GetProperty("generated_result").GetString() ?? "";
            state.IsApproved = true; // Auto-approve translations
            Console.WriteLine($"[{NodeName} Output]:\n{state.GeneratedContent}");
        }
    }</pre>
<h2 data-path-to-node="23">4. The Hub Router (Orchestrator Node)</h2>
<p data-path-to-node="24">The <b data-path-to-node="24" data-index-in-node="4">Hub-and-Spoke Pattern</b> relies on a central router. The <code data-path-to-node="24" data-index-in-node="58">OrchestratorNode</code> acts as this hub. It evaluates the current <code data-path-to-node="24" data-index-in-node="118">AgentState</code> variables to logically deduce the workflow status (<code data-path-to-node="24" data-index-in-node="180">NEEDS_GENERATION</code>, <code data-path-to-node="24" data-index-in-node="198">NEEDS_EVALUATION</code>, <code data-path-to-node="24" data-index-in-node="216">NEEDS_REVISION</code>, or <code data-path-to-node="24" data-index-in-node="235">APPROVED</code>). It then tells the execution runner which &#8220;spoke&#8221; to trigger next.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="csharp">public class OrchestratorNode : AgentNodeBase
    {
        public OrchestratorNode(IChatModel chatModel) : base(chatModel) { }

        protected override string NodeName =&gt; "Orchestrator";

        protected override string GetSystemPrompt() =&gt; @"
            You are the Lead Orchestrator Agent. Your job is to route the workflow based strictly on the 'Workflow Status' provided.
            
            Routing Rules:
            - If Workflow Status is 'NEEDS_GENERATION', route to: 'generator'
            - If Workflow Status is 'NEEDS_EVALUATION', route to: 'evaluator'
            - If Workflow Status is 'NEEDS_REVISION', route to: 'generator'
            - If Workflow Status is 'APPROVED', route to: 'finish'
            - If the user explicitly asks for a language translation and the status is NEEDS_GENERATION, route to: 'translator'
            
            Respond strictly in valid JSON format:
            { ""route_to"": ""chosen_route"", ""reasoning"": ""brief explanation of why"" }";

        protected override string GetHumanPrompt(AgentState state)
        {
            // Calculate a strict, foolproof status string for the LLM
            string workflowStatus = "NEEDS_GENERATION";

            if (state.IsApproved)
            {
                workflowStatus = "APPROVED";
            }
            else if (!string.IsNullOrWhiteSpace(state.GeneratedContent))
            {
                if (string.IsNullOrWhiteSpace(state.EvaluatorFeedback))
                {
                    // Content exists, but no feedback yet -&gt; Must Evaluate
                    workflowStatus = "NEEDS_EVALUATION";
                }
                else
                {
                    // Content exists, and we have feedback -&gt; Must Revise
                    workflowStatus = "NEEDS_REVISION";
                }
            }

            string genContent = string.IsNullOrWhiteSpace(state.GeneratedContent) ? "None" : state.GeneratedContent;
            string evalFeedback = string.IsNullOrWhiteSpace(state.EvaluatorFeedback) ? "None" : state.EvaluatorFeedback;

            return $@"
            Workflow Status: {workflowStatus}
            Original Request: {state.OriginalRequest}
            Generated Content: {genContent}
            Evaluator Feedback: {evalFeedback}";
        }

        protected override void ParseAndUpdateState(AgentState state, JsonElement jsonRoot)
        {
            state.OrchestratorRoute = jsonRoot.GetProperty("route_to").GetString()?.ToLower() ?? "unknown";
            string reasoning = jsonRoot.GetProperty("reasoning").GetString() ?? "";

            Console.WriteLine($"[{NodeName} Decision]: Route to '{state.OrchestratorRoute}'");
            Console.WriteLine($"[{NodeName} Reasoning]: {reasoning}");
        }
    }</pre>
<h2 data-path-to-node="26">5. The State Machine Runner</h2>
<p data-path-to-node="27">The <code data-path-to-node="27" data-index-in-node="4">AgentGraphRunner</code> executes the actual Hub-and-Spoke loop. It ensures that every single step passes through the Orchestrator first. Crucially, it implements a <code data-path-to-node="27" data-index-in-node="161">maxLoops</code> failsafe to prevent the agents from getting stuck in an infinite loop of generation and rejection.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="csharp">// ==========================================
    // WORKFLOW RUNNER (HUB-AND-SPOKE STATE MACHINE)
    // ==========================================
    public class AgentGraphRunner
    {
        private readonly OrchestratorNode _orchestrator;
        private readonly TranslatorNode _translator;
        private readonly GeneratorNode _generator;
        private readonly EvaluatorNode _evaluator;

        public AgentGraphRunner(IChatModel chatModel)
        {
            _orchestrator = new OrchestratorNode(chatModel);
            _translator = new TranslatorNode(chatModel);
            _generator = new GeneratorNode(chatModel);
            _evaluator = new EvaluatorNode(chatModel);
        }

        public async Task RunWorkflowAsync(string userRequest)
        {
            var state = new AgentState { OriginalRequest = userRequest };

            int maxLoops = 10; // Failsafe to prevent endless LLM arguments
            int currentLoop = 0;

            Console.WriteLine("\n=== Starting Hub-and-Spoke Agentic Workflow ===");

            while (currentLoop &lt; maxLoops)
            {
                // 1. The Orchestrator acts as the central router for EVERY step
                state = await _orchestrator.ExecuteAsync(state);

                // 2. Execute the route determined by the Orchestrator
                if (state.OrchestratorRoute == "finish")
                {
                    Console.WriteLine("\n[System] Orchestrator determined the task is complete.");
                    break;
                }
                else if (state.OrchestratorRoute == "translator")
                {
                    state = await _translator.ExecuteAsync(state);
                }
                else if (state.OrchestratorRoute == "generator")
                {
                    state = await _generator.ExecuteAsync(state);
                }
                else if (state.OrchestratorRoute == "evaluator")
                {
                    state = await _evaluator.ExecuteAsync(state);
                }
                else
                {
                    Console.WriteLine($"\n[System] Unknown route '{state.OrchestratorRoute}'. Ending workflow.");
                    break;
                }

                currentLoop++;
            }

            if (currentLoop &gt;= maxLoops)
            {
                Console.WriteLine($"\n[System] Workflow forced to stop after {maxLoops} iterations.");
            }

            Console.WriteLine("\n=== Workflow Complete ===");
            Console.WriteLine(state.ToJson());
        }
    }</pre>
<h2 data-path-to-node="29">6. Application Initialization</h2>
<p data-path-to-node="30">Finally, the <code data-path-to-node="30" data-index-in-node="13">Main</code> execution initializes the model provider and launches the interactive loop.</p>
<p data-path-to-node="31"><b data-path-to-node="31" data-index-in-node="0">Critical Architecture Note:</b> The current implementation utilizes an identical local model (<code data-path-to-node="31" data-index-in-node="90">gemma3:4b</code>) for both generation and evaluation. Operating the same model for generation and validation introduces structural bias and validation echo chambers. In a production environment, <b data-path-to-node="31" data-index-in-node="278">decoupling model providers is mandatory</b>. The <code data-path-to-node="31" data-index-in-node="323">EvaluatorNode</code> should ideally be initialized with a secondary, more advanced model architecture to ensure objective review and mathematical correctness.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="csharp">class Program
    {
        static async Task Main(string[] args)
        {
            // 1. Force UTF-8 Encoding for Traditional Chinese / Unicode support
            Console.OutputEncoding = System.Text.Encoding.UTF8;
            Console.InputEncoding = System.Text.Encoding.UTF8;

            // 2. Initialize Provider and Model (OpenAI compatibility mode for Ollama)
            // Note: Endpoints must be secured/masked in production.
            var provider = new OpenAiProvider(
                apiKey: "ollama",
                customEndpoint: "http://[REDACTED_INTERNAL_IP]:11434/v1" 
            );
            
            // To prevent structural bias, consider instantiating a second model 
            // (e.g., GPT-4o or Claude 3.5 Sonnet) specifically for the Evaluator node.
            var chatModel = new OpenAiChatModel(provider, id: "gemma3:4b");

            // 3. Instantiate the Runner
            var runner = new AgentGraphRunner(chatModel);

            Console.WriteLine("=================================================");
            Console.WriteLine("🤖 OOP Multi-Agent Workflow Initialized");
            Console.WriteLine("Type 'exit' to quit.");
            Console.WriteLine("=================================================\n");

            // 4. Interactive User Loop
            while (true)
            {
                Console.Write("👤 You: ");
                string? userRequest = Console.ReadLine();

                if (string.IsNullOrWhiteSpace(userRequest)) continue;
                if (userRequest.Trim().ToLower() is "exit" or "quit") break;

                try
                {
                    await runner.RunWorkflowAsync(userRequest);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"\n[Error] The workflow encountered an issue: {ex.Message}");
                }

                Console.WriteLine("\n------------------------------------------\n");
            }
        }
    }
}</pre>
<h3 data-path-to-node="29">Test Result</h3>
<p><img data-recalc-dims="1" decoding="async" class="alignnone size-full wp-image-1817" src="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Screenshot-2026-04-15-085010.png?resize=678%2C556&#038;ssl=1" alt="" width="678" height="556" srcset="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Screenshot-2026-04-15-085010.png?w=1728&amp;ssl=1 1728w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Screenshot-2026-04-15-085010.png?resize=300%2C246&amp;ssl=1 300w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Screenshot-2026-04-15-085010.png?resize=1024%2C840&amp;ssl=1 1024w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Screenshot-2026-04-15-085010.png?resize=768%2C630&amp;ssl=1 768w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Screenshot-2026-04-15-085010.png?resize=1536%2C1260&amp;ssl=1 1536w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2026/04/Screenshot-2026-04-15-085010.png?w=1356&amp;ssl=1 1356w" sizes="(max-width: 678px) 100vw, 678px" /></p>
<h3 data-path-to-node="29">References</h3>
<ol start="1" data-path-to-node="30">
<li>
<p data-path-to-node="30,0,0">Ratnesh Yadav. (2025). <i data-path-to-node="30,0,0" data-index-in-node="23">Bot-to-Bot: Centralized (Hub-and-Spoke) Multi-Agent Topology</i>. Medium. <a class="ng-star-inserted" href="https://medium.com/@ratneshyadav_26063/bot-to-bot-centralized-hub-and-spoke-multi-agent-topology-part-2-87b46ec7e1bc" target="_blank" rel="noopener" data-hveid="0" data-ved="0CAAQ_4QMahgKEwjX07ydn--TAxUAAAAAHQAAAAAQiwE">https://medium.com/@ratneshyadav_26063/bot-to-bot-centralized-hub-and-spoke-multi-agent-topology-part-2-87b46ec7e1bc</a></p>
</li>
<li>
<p data-path-to-node="30,1,0">Yuval Mehta. (2026). <i data-path-to-node="30,1,0" data-index-in-node="21">How Multi-Agent Self-Verification Actually Works</i>. Towards AI. <a class="ng-star-inserted" href="https://pub.towardsai.net/how-multi-agent-self-verification-actually-works-and-why-it-changes-everything-for-production-ai-71923df63d01" target="_blank" rel="noopener" data-hveid="0" data-ved="0CAAQ_4QMahgKEwjX07ydn--TAxUAAAAAHQAAAAAQjAE">https://pub.towardsai.net/how-multi-agent-self-verification-actually-works-and-why-it-changes-everything-for-production-ai-71923df63d01</a></p>
</li>
<li>
<p data-path-to-node="30,2,0">arXiv preprint. (2025). <i data-path-to-node="30,2,0" data-index-in-node="24">MAR: Multi-Agent Reflexion Improves Reasoning Abilities in LLMs</i>. arXiv. <a class="ng-star-inserted" href="https://arxiv.org/html/2512.20845v1" target="_blank" rel="noopener" data-hveid="0" data-ved="0CAAQ_4QMahgKEwjX07ydn--TAxUAAAAAHQAAAAAQjQE">https://arxiv.org/html/2512.20845v1</a></p>
</li>
</ol>
]]></content:encoded>
					
					<wfw:commentRss>https://www.chunho-ling.com/architectural-analysis-multi-agent-design-patterns-in-c-llm-workflows/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1816</post-id>	</item>
		<item>
		<title>[Terraform] Managing Vault Secrets via Terraform and OpenTofu: A K3s Token Implementation</title>
		<link>https://www.chunho-ling.com/terraform-managing-vault-secrets-via-terraform-and-opentofu-a-k3s-token-implementation/</link>
					<comments>https://www.chunho-ling.com/terraform-managing-vault-secrets-via-terraform-and-opentofu-a-k3s-token-implementation/#respond</comments>
		
		<dc:creator><![CDATA[C.H. Ling]]></dc:creator>
		<pubDate>Sat, 11 Apr 2026 12:41:44 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Terraform]]></category>
		<guid isPermaLink="false">https://www.chunho-ling.com/?p=1813</guid>

					<description><![CDATA[Storing and externalizing sensitive data is a foundational architectural pattern for secure platforms and applications. The following documentation demonstrates the procedure for setting and retrieving secrets within HashiCorp Vault utilizing Terraform or OpenTofu on deploying <a class="mh-excerpt-more" href="https://www.chunho-ling.com/terraform-managing-vault-secrets-via-terraform-and-opentofu-a-k3s-token-implementation/" title="[Terraform] Managing Vault Secrets via Terraform and OpenTofu: A K3s Token Implementation">[...]</a>]]></description>
										<content:encoded><![CDATA[<p>Storing and externalizing sensitive data is a foundational architectural pattern for secure platforms and applications. The following documentation demonstrates the procedure for setting and retrieving secrets within HashiCorp Vault utilizing Terraform or OpenTofu on deploying k3s node.<span id="more-1813"></span></p>
<h2 data-path-to-node="2">1. Provider Configuration</h2>
<p data-path-to-node="3">To interact with Vault and generate secure tokens, the environment must specify the required providers.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">terraform {
  required_providers {
    vault = {
      source  = "hashicorp/vault"
    }
    random = {
      source = "hashicorp/random"
    }
  }
}</pre>
<h2 data-path-to-node="5">2. Generating the Secret Data</h2>
<p data-path-to-node="6">In this implementation, a token is required to bootstrap a K3s cluster. The <code data-path-to-node="6" data-index-in-node="76">random</code> provider is utilized to generate a secure, 32-character string.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">resource "random_password" "k3s_cluster_token" {
  length  = 32
  special = false
}</pre>
<h2 data-path-to-node="8">3. Storing Sensitive Data in Vault (Set)</h2>
<p data-path-to-node="9">The generated token is injected into Vault using the <code data-path-to-node="9" data-index-in-node="53">vault_kv_secret_v2</code> resource. This operation assumes the target Vault mount path is configured for KV Version 2.</p>
<ul data-path-to-node="10">
<li>
<p data-path-to-node="10,0,0"><b data-path-to-node="10,0,0" data-index-in-node="0">Mount Point:</b> <code data-path-to-node="10,0,0" data-index-in-node="13">home-lab-secrets</code></p>
</li>
<li>
<p data-path-to-node="10,1,0"><b data-path-to-node="10,1,0" data-index-in-node="0">Secret Path:</b> <code data-path-to-node="10,1,0" data-index-in-node="13">k3s-cluster-token</code></p>
</li>
<li>
<p data-path-to-node="10,2,0"><b data-path-to-node="10,2,0" data-index-in-node="0">Payload Format:</b> JSON encoded key-value pair.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">resource "vault_kv_secret_v2" "k3s_cluster_token" {
  mount     = "home-lab-secrets"
  name      = "k3s-cluster-token"
  data_json = jsonencode({
    token = random_password.k3s_cluster_token.result
  })
}</pre>
</li>
</ul>
<h2 data-path-to-node="12">4. Retrieving Sensitive Data from Vault (Get)</h2>
<p data-path-to-node="13">To utilize the stored secret elsewhere in the infrastructure code, a Terraform data source fetches the values from Vault. The <code data-path-to-node="13" data-index-in-node="126">depends_on</code> meta-argument ensures the secret is fully provisioned before retrieval is attempted.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">data "vault_kv_secret_v2" "k3s_cluster_token" {
  depends_on = [vault_kv_secret_v2.k3s_cluster_token]
  mount      = "home-lab-secrets"
  name       = "k3s-cluster-token"
}</pre>
<h2 data-path-to-node="15">5. Usage Example: Bootstrapping a Node</h2>
<p data-path-to-node="16">Once retrieved, the secret data can be accessed via <code data-path-to-node="16" data-index-in-node="52">data.vault_kv_secret_v2.&lt;name&gt;.data.&lt;key&gt;</code>. In the provided example, the token is passed as an environment variable (<code data-path-to-node="16" data-index-in-node="168">K3S_TOKEN</code>) over an SSH connection using a <code data-path-to-node="16" data-index-in-node="210">remote-exec</code> provisioner to join a node to the existing K3s cluster.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">provisioner "remote-exec" {
    inline = [
      "cloud-init status --wait",
      "curl -sfL https://get.k3s.io | K3S_URL='https://${local.master_node.network.ip}:6443' K3S_TOKEN='${data.vault_kv_secret_v2.k3s_cluster_token.data.token}' sh -"
    ]
  }</pre>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.chunho-ling.com/terraform-managing-vault-secrets-via-terraform-and-opentofu-a-k3s-token-implementation/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1813</post-id>	</item>
		<item>
		<title>[Proxmox] Setup proxmox cluster and join node</title>
		<link>https://www.chunho-ling.com/proxmox-setup-proxmox-cluster-and-join-node/</link>
					<comments>https://www.chunho-ling.com/proxmox-setup-proxmox-cluster-and-join-node/#respond</comments>
		
		<dc:creator><![CDATA[C.H. Ling]]></dc:creator>
		<pubDate>Thu, 10 Oct 2024 21:15:35 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[Infrastructure 基建]]></category>
		<category><![CDATA[Proxmox]]></category>
		<category><![CDATA[Server 伺服器]]></category>
		<category><![CDATA[Virtualization 虛擬化]]></category>
		<guid isPermaLink="false">https://www.chunho-ling.com/?p=1740</guid>

					<description><![CDATA[It is easy to find many material on how to setup cluster, but sometime it will have variance issues when join cluster. Lesson learnt from previous work, I will conclude them into steps and hope <a class="mh-excerpt-more" href="https://www.chunho-ling.com/proxmox-setup-proxmox-cluster-and-join-node/" title="[Proxmox] Setup proxmox cluster and join node">[...]</a>]]></description>
										<content:encoded><![CDATA[<p>It is easy to find many material on how to setup cluster, but sometime it will have variance issues when join cluster.</p>
<p>Lesson learnt from previous work, I will conclude them into steps and hope it can help others.<span id="more-1740"></span>Prerequisites</p>
<ol>
<li>Ensure all Proxmox nodes are same version.<br />
It can done by run commands in each node below:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">## Update package list.
sudo apt update

## Ensure all proxmox node in same version.
sudo apt dist-upgrade -y 

## Update package.
sudo apt upgrade

## Clean up
sudo apt clean</pre>
</li>
<li>Ensure all node connect to network;</li>
<li>Ensure all node can resolve each other&#8217;s DNS record;</li>
</ol>
<p>In there, it will use 2 nodes (nodeA, nodeB) in command as sample/</p>
<p>Steps:</p>
<ol>
<li>Create cluster.<br />
In nodeA, use command below to create cluster.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">pvecm create demo-cluster</pre>
</li>
<li>Grand cluster firewall port.<br />
In nodeA, open /etc/pve/firewall/cluster.fw , add rules inside section [RULES], sample as below:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">[RULES]

IN SSH(ACCEPT) -i vmbr0 -log nolog # SSH between nodes
IN NTP(ACCEPT) -i vmbr0 -log nolog # NTP sync time
IN DNS(ACCEPT) -i vmbr0 -log nolog # DNS resolve
IN ACCEPT -i vmbr0 -p tcp -dport 5403 -sport 5403 -log nolog # Proxmox VE Cluster Daemon
IN ACCEPT -i vmbr0 -p tcp -dport 60050 -sport 60000 -log nolog # Proxmox VE backup and migration traffic
IN ACCEPT -i vmbr0 -p udp -dport 5405 -sport 5404 -log nolog # Proxmox corosync
IN ACCEPT -i vmbr0 -p tcp -dport 3128 -sport 3128 -log nolog # Proxmox VE HA (High Availability) manager
IN ACCEPT -i vmbr0 -p tcp -dport 5999 -sport 5900 -log nolog # SPICE console connections.
IN ACCEPT -i vmbr0 -p tcp -dport 8009 -sport 8007 -log nolog # Proxmox VE cluster traffic
IN ACCEPT -i vmbr0 -p tcp -dport 8006 -sport 8006 -log nolog # Proxmox VM</pre>
</li>
<li>Join node.<br />
In nodeB, use command below to join cluster.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">pvecm add &lt;node1 IP address&gt;</pre>
</li>
<li>Verify result<br />
In nodeB, run command below, if it can found nodeB IP address, means node join successfully.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">pvecm status</pre>
<p><img data-recalc-dims="1" loading="lazy" decoding="async" class="alignnone size-full wp-image-1741" src="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/Screenshot-2024-10-09-221017.png?resize=678%2C617&#038;ssl=1" alt="" width="678" height="617" srcset="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/Screenshot-2024-10-09-221017.png?w=1021&amp;ssl=1 1021w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/Screenshot-2024-10-09-221017.png?resize=300%2C273&amp;ssl=1 300w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/Screenshot-2024-10-09-221017.png?resize=768%2C699&amp;ssl=1 768w" sizes="auto, (max-width: 678px) 100vw, 678px" /></li>
</ol>
]]></content:encoded>
					
					<wfw:commentRss>https://www.chunho-ling.com/proxmox-setup-proxmox-cluster-and-join-node/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1740</post-id>	</item>
		<item>
		<title>[Git] Troubleshoot on unable to read tree ()</title>
		<link>https://www.chunho-ling.com/git-troubleshoot-on-unable-to-read-tree/</link>
					<comments>https://www.chunho-ling.com/git-troubleshoot-on-unable-to-read-tree/#respond</comments>
		
		<dc:creator><![CDATA[C.H. Ling]]></dc:creator>
		<pubDate>Wed, 09 Oct 2024 20:18:54 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[Git]]></category>
		<guid isPermaLink="false">https://www.chunho-ling.com/?p=1734</guid>

					<description><![CDATA[When git checkout commit, found error Fix Unable to read tree like that. It cause by dangling commit and dangling blob in repository. Dangling commit:  Commit that isn&#8217;t directly linked to by any child commit, <a class="mh-excerpt-more" href="https://www.chunho-ling.com/git-troubleshoot-on-unable-to-read-tree/" title="[Git] Troubleshoot on unable to read tree ()">[...]</a>]]></description>
										<content:encoded><![CDATA[<p>When git checkout commit, found error Fix Unable to read tree like that.</p>
<p><img data-recalc-dims="1" loading="lazy" decoding="async" class="alignnone size-full wp-image-1735" src="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/2024-10-04-15_08_56-Untitled-Paint.png?resize=678%2C204&#038;ssl=1" alt="" width="678" height="204" srcset="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/2024-10-04-15_08_56-Untitled-Paint.png?w=1127&amp;ssl=1 1127w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/2024-10-04-15_08_56-Untitled-Paint.png?resize=300%2C90&amp;ssl=1 300w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/2024-10-04-15_08_56-Untitled-Paint.png?resize=1024%2C308&amp;ssl=1 1024w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/2024-10-04-15_08_56-Untitled-Paint.png?resize=768%2C231&amp;ssl=1 768w" sizes="auto, (max-width: 678px) 100vw, 678px" /><span id="more-1734"></span></p>
<p>It cause by dangling commit and dangling blob in repository.</p>
<p>Dangling commit:  Commit that isn&#8217;t directly linked to by any child commit, branch, tag or other reference</p>
<p>Dangling blob: A change that made it to the staging area/index, but never got committed.</p>
<p>These dragging commit and blob can found in command below:</p>
<pre data-bidi-marker="true"><code class="language-batch">git fsck --name-objects
</code></pre>
<p>Output:</p>
<p>&nbsp;</p>
<p><img data-recalc-dims="1" loading="lazy" decoding="async" class="alignnone size-full wp-image-1736" src="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/2024-10-04-15_11_51-Fix-unable-to-read-tree-__Committed-ID__-Troika-Confluence-%E2%80%94-Mozilla-Firef.png?resize=678%2C790&#038;ssl=1" alt="" width="678" height="790" srcset="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/2024-10-04-15_11_51-Fix-unable-to-read-tree-__Committed-ID__-Troika-Confluence-%E2%80%94-Mozilla-Firef.png?w=883&amp;ssl=1 883w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/2024-10-04-15_11_51-Fix-unable-to-read-tree-__Committed-ID__-Troika-Confluence-%E2%80%94-Mozilla-Firef.png?resize=257%2C300&amp;ssl=1 257w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/2024-10-04-15_11_51-Fix-unable-to-read-tree-__Committed-ID__-Troika-Confluence-%E2%80%94-Mozilla-Firef.png?resize=879%2C1024&amp;ssl=1 879w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/10/2024-10-04-15_11_51-Fix-unable-to-read-tree-__Committed-ID__-Troika-Confluence-%E2%80%94-Mozilla-Firef.png?resize=768%2C895&amp;ssl=1 768w" sizes="auto, (max-width: 678px) 100vw, 678px" /></p>
<p>Run command below to clean danging commit / blob and clean up repository</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">cd $REPO_PATH 
## Remove all danging commit and blob and GC collect. 
git reflog expire --expire=now --all 
git gc --prune=now 

## Clean up local old / conflict branch 
git remote prune origin</pre>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.chunho-ling.com/git-troubleshoot-on-unable-to-read-tree/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1734</post-id>	</item>
		<item>
		<title>[Java] Validate constrain in code level</title>
		<link>https://www.chunho-ling.com/java-validate-constrain-in-code-level/</link>
					<comments>https://www.chunho-ling.com/java-validate-constrain-in-code-level/#respond</comments>
		
		<dc:creator><![CDATA[C.H. Ling]]></dc:creator>
		<pubDate>Fri, 29 Mar 2024 11:57:04 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<guid isPermaLink="false">https://www.chunho-ling.com/?p=1667</guid>

					<description><![CDATA[For DTO validation, normally it will implement in different class property and trigger with @Validate annotation. However, it will not be trigger when method call due to performance issue. We can trigger the checking in <a class="mh-excerpt-more" href="https://www.chunho-ling.com/java-validate-constrain-in-code-level/" title="[Java] Validate constrain in code level">[...]</a>]]></description>
										<content:encoded><![CDATA[<p>For DTO validation, normally it will implement in different class property and trigger with @Validate annotation. However, it will not be trigger when method call due to performance issue.</p>
<p>We can trigger the checking in code. And it is recommend for API response after collect API response.</p>
<p><span id="more-1667"></span></p>
<p>Code to validate response in code level, it can be move to abstract class or interface and call it when needed.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="java">import javax.validation.ConstraintViolation;

ApiResponse response = ApiClient.get();
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set&lt;ConstraintViolation&lt;ApiResponse&gt;&gt; result = validator.validate(response);
if(!result.isEmpty()) {
  String message = result.stream()
  .map(o-&gt; new StringBuilder()
  .append(o.getPropertyPath().toString())
  .append(o.getMessage()))
  .collect(Collectors.joining(";\r\n"));
  throw new IllegalArgumentException(message);
}</pre>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.chunho-ling.com/java-validate-constrain-in-code-level/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1667</post-id>	</item>
		<item>
		<title>[IntelliJ] Create custom code snippet</title>
		<link>https://www.chunho-ling.com/intellij-create-custom-code-snippet/</link>
					<comments>https://www.chunho-ling.com/intellij-create-custom-code-snippet/#respond</comments>
		
		<dc:creator><![CDATA[C.H. Ling]]></dc:creator>
		<pubDate>Thu, 28 Mar 2024 11:52:58 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[IntelliJ]]></category>
		<category><![CDATA[Programming]]></category>
		<guid isPermaLink="false">https://www.chunho-ling.com/?p=1699</guid>

					<description><![CDATA[To standardize code style among different project and teams, snippet is one of the tools to implement coding standard. However, snippet is the two-side blade for developer. It will introduce duplicate code and result difficult <a class="mh-excerpt-more" href="https://www.chunho-ling.com/intellij-create-custom-code-snippet/" title="[IntelliJ] Create custom code snippet">[...]</a>]]></description>
										<content:encoded><![CDATA[<p>To standardize code style among different project and teams, snippet is one of the tools to implement coding standard.</p>
<p>However, snippet is the two-side blade for developer. It will introduce duplicate code and result difficult to maintain if abuse. The recommend place to use snippets are:</p>
<ol>
<li>Unit test for high level format;</li>
<li>DTO / DAO with specific handling in getter / setter method;</li>
<li>Predefined markup (e.g. XML / HTML, JSON, YAML);</li>
</ol>
<p>In this demo, it will use JUnit parameter test as sample to create snippet.<span id="more-1699"></span>Steps:</p>
<ol>
<li>Create Live Template<br />
Highlight code would like to add into snippet, press Control + Shift + A, and input <strong>save as live template</strong>, then click Enter.<img data-recalc-dims="1" loading="lazy" decoding="async" class="alignnone size-full wp-image-1700" src="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_42_21-Window.png?resize=678%2C508&#038;ssl=1" alt="" width="678" height="508" srcset="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_42_21-Window.png?w=1055&amp;ssl=1 1055w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_42_21-Window.png?resize=300%2C225&amp;ssl=1 300w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_42_21-Window.png?resize=1024%2C768&amp;ssl=1 1024w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_42_21-Window.png?resize=768%2C576&amp;ssl=1 768w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_42_21-Window.png?resize=678%2C509&amp;ssl=1 678w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_42_21-Window.png?resize=326%2C245&amp;ssl=1 326w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_42_21-Window.png?resize=80%2C60&amp;ssl=1 80w" sizes="auto, (max-width: 678px) 100vw, 678px" /></li>
<li>Add snippet abbreviation and variables.<br />
In Save as Live Template dialog, in <strong>Abbreviation</strong>, input name <strong>ptest</strong>, and variables are format like <strong>$variableName$</strong>.<br />
<img data-recalc-dims="1" loading="lazy" decoding="async" class="alignnone size-mhmagazinecontent wp-image-1701" src="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_43_39-Window.png?resize=678%2C429&#038;ssl=1" alt="" width="678" height="429" srcset="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_43_39-Window.png?w=1564&amp;ssl=1 1564w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_43_39-Window.png?resize=300%2C190&amp;ssl=1 300w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_43_39-Window.png?resize=1024%2C648&amp;ssl=1 1024w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_43_39-Window.png?resize=768%2C486&amp;ssl=1 768w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_43_39-Window.png?resize=1536%2C972&amp;ssl=1 1536w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_43_39-Window.png?w=1356&amp;ssl=1 1356w" sizes="auto, (max-width: 678px) 100vw, 678px" /></li>
<li>Configure snippet variables.<br />
Click Edit Variables, in expression, input default value. You can use method provided to customize value (e.g. upper case, camelCase, etc) to fit your need.<br />
<img data-recalc-dims="1" loading="lazy" decoding="async" class="alignnone size-mhmagazinecontent wp-image-1702" src="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_43_58-Window.png?resize=650%2C353&#038;ssl=1" alt="" width="650" height="353" srcset="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_43_58-Window.png?w=650&amp;ssl=1 650w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_43_58-Window.png?resize=300%2C163&amp;ssl=1 300w" sizes="auto, (max-width: 650px) 100vw, 650px" /></li>
<li>Test snippet.<br />
In project, type snippet abbreviation, expected it will show in intelli-sense, after press enter, snippet will be render.<img data-recalc-dims="1" loading="lazy" decoding="async" class="alignnone size-mhmagazinecontent wp-image-1704" src="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_44_31-Window.png?resize=678%2C165&#038;ssl=1" alt="" width="678" height="165" srcset="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_44_31-Window.png?w=694&amp;ssl=1 694w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_44_31-Window.png?resize=300%2C73&amp;ssl=1 300w" sizes="auto, (max-width: 678px) 100vw, 678px" /> <img data-recalc-dims="1" loading="lazy" decoding="async" class="alignnone size-mhmagazinecontent wp-image-1705" src="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_45_27-Amazon-WorkSpaces.png?resize=678%2C483&#038;ssl=1" alt="" width="678" height="483" srcset="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_45_27-Amazon-WorkSpaces.png?w=807&amp;ssl=1 807w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_45_27-Amazon-WorkSpaces.png?resize=300%2C214&amp;ssl=1 300w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/2024-03-26-15_45_27-Amazon-WorkSpaces.png?resize=768%2C547&amp;ssl=1 768w" sizes="auto, (max-width: 678px) 100vw, 678px" /></li>
</ol>
]]></content:encoded>
					
					<wfw:commentRss>https://www.chunho-ling.com/intellij-create-custom-code-snippet/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1699</post-id>	</item>
		<item>
		<title>[GitHub Action] Get gradle package version</title>
		<link>https://www.chunho-ling.com/github-action-get-gradle-package-version/</link>
					<comments>https://www.chunho-ling.com/github-action-get-gradle-package-version/#respond</comments>
		
		<dc:creator><![CDATA[C.H. Ling]]></dc:creator>
		<pubDate>Tue, 12 Mar 2024 19:59:56 +0000</pubDate>
				<category><![CDATA[Automation]]></category>
		<category><![CDATA[CI / CD]]></category>
		<category><![CDATA[Computing]]></category>
		<category><![CDATA[DevOps]]></category>
		<guid isPermaLink="false">https://www.chunho-ling.com/?p=1691</guid>

					<description><![CDATA[For executing pipeline, it is common to get package version. One of the patrice is mark the package version in package info file. In this example, it will use Gradle as example to show how <a class="mh-excerpt-more" href="https://www.chunho-ling.com/github-action-get-gradle-package-version/" title="[GitHub Action] Get gradle package version">[...]</a>]]></description>
										<content:encoded><![CDATA[<p>For executing pipeline, it is common to get package version. One of the patrice is mark the package version in package info file.</p>
<p>In this example, it will use Gradle as example to show how to get package version and call it in in other steps.<span id="more-1691"></span></p>
<p>Steps</p>
<ol>
<li>Update GitHub Action.<br />
Add steps below in GiutHub action.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">- name: Read package version
  id: get-version
  run: |
    version=$(grep -oP "version\s*=\s*'[^']+'" build.gradle | grep -oP "[^']*" | tr -d '\n' | tr -d ' ' | tr -d 'version=')
    echo "::set-output name=version::${version}"

- name: Print package version
  run: |
    echo "Testing current version."
    echo "Package version is ${{ steps.get-version.outputs.version }}"</pre>
</li>
<li>Check result<br />
Execute the pipeline to check result, expect version in build.gradle mentioned will shown.<img data-recalc-dims="1" loading="lazy" decoding="async" class="alignnone size-full wp-image-1697" src="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/Screenshot-2024-03-11-213059.png?resize=678%2C178&#038;ssl=1" alt="" width="678" height="178" srcset="https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/Screenshot-2024-03-11-213059.png?w=1700&amp;ssl=1 1700w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/Screenshot-2024-03-11-213059.png?resize=300%2C79&amp;ssl=1 300w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/Screenshot-2024-03-11-213059.png?resize=1024%2C269&amp;ssl=1 1024w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/Screenshot-2024-03-11-213059.png?resize=768%2C201&amp;ssl=1 768w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/Screenshot-2024-03-11-213059.png?resize=1536%2C403&amp;ssl=1 1536w, https://i0.wp.com/www.chunho-ling.com/wp-content/uploads/2024/03/Screenshot-2024-03-11-213059.png?w=1356&amp;ssl=1 1356w" sizes="auto, (max-width: 678px) 100vw, 678px" /></li>
</ol>
]]></content:encoded>
					
					<wfw:commentRss>https://www.chunho-ling.com/github-action-get-gradle-package-version/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1691</post-id>	</item>
	</channel>
</rss>
