<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://bash-365.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://bash-365.com/" rel="alternate" type="text/html" /><updated>2026-07-12T21:59:41+00:00</updated><id>https://bash-365.com/feed.xml</id><title type="html">BASH Consultants</title><subtitle>Enterprise-grade IT for small business — cloud, ERP, data, and AI-augmented automation from Denver, Colorado</subtitle><author><name>Amr</name></author><entry><title type="html">When the numbers say it’s time to leave QuickBooks</title><link href="https://bash-365.com/posts/2026/07/08/when-the-numbers-say-its-time-to-leave-quickbooks/" rel="alternate" type="text/html" title="When the numbers say it’s time to leave QuickBooks" /><published>2026-07-08T12:00:00+00:00</published><updated>2026-07-08T12:00:00+00:00</updated><id>https://bash-365.com/posts/2026/07/08/when-the-numbers-say-its-time-to-leave-quickbooks</id><content type="html" xml:base="https://bash-365.com/posts/2026/07/08/when-the-numbers-say-its-time-to-leave-quickbooks/"><![CDATA[<p>Nobody leaves QuickBooks because they dislike it. They leave because the spreadsheet that patches its gaps has quietly become the real accounting system — and one month, reconciling that spreadsheet costs more than the migration would have.</p>

<h2 id="the-decision-belongs-to-finance-not-it">The decision belongs to finance, not IT</h2>

<p>Whether to move off QuickBooks is not a technology question. It is a spend decision with a return you can calculate, and the person who signs the close is the one who should own it. The trap is treating it as a preference — a nicer interface, a newer vendor — when it is really a threshold problem. Below the threshold, QuickBooks plus discipline is the right, cheap answer. Above it, staying costs more every month than moving would, and the cost hides in labor and risk instead of on an invoice.</p>

<p>So the useful question isn’t “is QuickBooks good enough?” It’s “what is the status quo actually costing us, and has that number crossed the line yet?” Everything below is about finding that line.</p>

<h2 id="five-signals-youve-crossed-the-threshold">Five signals you’ve crossed the threshold</h2>

<p>These are the patterns that separate a real ceiling from a setup you configured badly. One of them alone rarely justifies a migration. Two or three together usually do.</p>

<ul>
  <li><strong>Two or more company files stitched together.</strong> The moment your books live in more than one QuickBooks file and someone merges them by hand, you are running a manual consolidation engine made of a person and a spreadsheet. That person is a single point of failure, and the spreadsheet is unauditable.</li>
  <li><strong>Transaction volume the file can’t carry.</strong> List limits, file bloat, and multi-minute report runs are the software telling you it was built for a smaller company than you now are. When users start avoiding reports because they’re slow, the data has stopped serving the business.</li>
  <li><strong>Close-time creep.</strong> Track how long month-end close takes, in days, over the last year. If it’s climbing — five days became eight, eight became twelve — you’re watching complexity outrun the tool. The close is the clearest vital sign finance has.</li>
  <li><strong>Multi-entity and consolidation pain.</strong> More than two legal entities and a need to see the consolidated picture on a schedule is the signal QuickBooks handles worst. It does not consolidate entities natively, so every reporting cycle you pay for it in manual labor. Vendor documentation is candid about where native consolidation and multi-book accounting live — see Oracle’s <a href="https://docs.oracle.com/en/cloud/saas/netsuite/index.html">NetSuite documentation</a> and Microsoft’s <a href="https://learn.microsoft.com/en-us/dynamics365/business-central/">Dynamics 365 Business Central documentation</a> for what that machinery looks like when it’s built in rather than bolted on.</li>
  <li><strong>Inventory or cost-accounting you’ve outgrown.</strong> If true costs live in a spreadsheet because QuickBooks can’t track landed cost, work-in-process, or standard-versus-actual, your margins are a monthly estimate. For a light manufacturer or a Front Range distributor, that estimate is the number the whole business steers by.</li>
</ul>

<h2 id="what-the-status-quo-actually-costs">What the status quo actually costs</h2>

<p>Here is the framing that turns this from opinion into arithmetic. The cost of staying is real; it’s just unbilled. Put a dollar figure on three lines and the decision usually makes itself.</p>

<ul>
  <li><strong>Manual reconciliation labor.</strong> How many hours a month does your team spend merging files, rebuilding consolidations, and reconciling the spreadsheet against the ledger? Multiply by a loaded hourly rate. This is a recurring cost that grows with you.</li>
  <li><strong>The delayed close.</strong> A close that runs long doesn’t just cost the days it takes. It delays every decision that waits on the numbers — pricing, hiring, borrowing — and it’s the first thing an auditor, a lender, or a buyer probes. Slow books quietly discount the value of the business.</li>
  <li><strong>Error risk.</strong> Every hand-keyed consolidation is a chance to be wrong in a way nobody catches until it matters. The cost isn’t the average month; it’s the tail — the restatement, the covenant miss, the tax adjustment — weighted by how likely it’s become.</li>
</ul>

<p>Add those up as an annual number. That’s your cost of inaction. It is the honest thing to weigh a migration against — not the QuickBooks subscription, which was never the expensive part.</p>

<h2 id="the-total-cost-of-the-move-in-ranges">The total cost of the move, in ranges</h2>

<p>A migration is a real project with a real budget, and anyone who quotes you a fixed price before seeing your data is guessing. Think in ranges across three buckets:</p>

<ul>
  <li><strong>Implementation.</strong> Configuration, chart-of-accounts redesign, process work, integrations, testing, training, and a parallel run before you trust the new system. This is the largest bucket and it scales with the number of entities, modules, and integrations — not with the vendor’s sticker price.</li>
  <li><strong>Data cleanup and migration.</strong> Mapping and moving history into a new structure. This is the bucket nobody budgets for, and more on it below.</li>
  <li><strong>Subscription.</strong> The ongoing platform cost, which is genuinely the smallest of the three over a project’s life.</li>
</ul>

<p>The timeline that governs the budget: a mid-market Enterprise Resource Planning (ERP) implementation typically runs about <strong>five to seven months</strong> from decision to go-live, and that assumes someone internal can own it without dropping their day job. The largest hidden cost of the whole project is that person’s time — the months your best operator spends on migration instead of running the business. It sinks more projects than the sticker price does. We work these numbers as scoped ranges, never a fixed quote, and you should be suspicious of anyone who does otherwise.</p>

<h2 id="inventory-before-you-decide">Inventory before you decide</h2>

<p>Before you evaluate a single platform, document your own numbers. This is the deterministic part of the decision, and it comes first — you cannot compare systems against requirements you haven’t written down. Write down, on one page — the same inventory the [[Outgrowing QuickBooks: is it time for ERP?]] guide walks through step by step:</p>

<ul>
  <li><strong>Entities and files.</strong> How many legal entities, how many QuickBooks files, and exactly how they get consolidated today.</li>
  <li><strong>Volume.</strong> Transactions per month, users, and the reports that have gotten slow.</li>
  <li><strong>The close.</strong> How many days it takes now, which steps eat the most days, and how that number has moved over twelve months.</li>
  <li><strong>The chart of accounts.</strong> Your current structure, and the segments or dimensions you wish you had.</li>
  <li><strong>The spreadsheets.</strong> Every workbook doing a job the accounting system should do — inventory, consolidation, job costing, allocations. Each one is a requirement in disguise.</li>
  <li><strong>Integrations.</strong> What feeds the books today — payroll, point-of-sale, a bank feed, a customer relationship system — and what would have to reconnect.</li>
</ul>

<p>That page is worth more than any vendor demo. It’s the artifact that lets you compare platforms against your reality instead of against a sales narrative, and it’s the difference between choosing a system and being sold one.</p>

<h2 id="the-target-depends-on-the-numbers-not-the-logo">The target depends on the numbers, not the logo</h2>

<p>There is no single right answer, and any advisor who leads with one before seeing your inventory is selling, not advising. Oracle NetSuite, Microsoft Dynamics 365 Business Central, and — for more companies than vendors like to admit — a well-configured QuickBooks with the spreadsheets retired are all legitimate destinations depending on entity count, inventory complexity, transaction volume, and how much internal capacity you have. The right target is whichever one your one-page inventory points to. We stay neutral on purpose and build systems you own and could leave with, because the point is a platform that fits your numbers, not a vendor you’re locked into.</p>

<h2 id="the-two-things-that-go-wrong">The two things that go wrong</h2>

<ul>
  <li><strong>The data cleanup nobody budgets for.</strong> Years of duplicate vendors, inconsistent item names, dead accounts, and mis-mapped history do not migrate clean. Someone has to decide what moves, what gets fixed, and what gets left behind — and that work is often bigger than the software configuration. Budget for it explicitly, up front, or it will surface mid-project as a schedule slip and a scramble.</li>
  <li><strong>The first close on the new system.</strong> The riskiest month is the first month-end you run for real on the new platform. Plan a parallel run — old and new side by side — and treat the first live close as the moment the project is actually done, not go-live day. The teams that skip the parallel run are the ones re-keying numbers in a panic on day three.</li>
</ul>

<h2 id="next-step">Next step</h2>

<p>If two or three of those signals are true and your cost-of-inaction number has crossed your migration number, the move is worth doing once and doing well. A scoped assessment — your one-page inventory, what you actually need, and whether to fix the model or replace it — is the cheapest way to avoid a six-figure mistake. See [[ERP consulting]] to start there.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="corp" /><category term="erp-implementation" /><category term="quickbooks" /><category term="total-cost-of-ownership" /><category term="multi-entity" /><category term="month-end-close" /><category term="vendor-lock-in" /><summary type="html"><![CDATA[The financial thresholds and cost-of-inaction signals that tell a CFO it's time for a QuickBooks-to-ERP migration, before the close breaks]]></summary></entry><entry><title type="html">An AI acceptable-use policy your team will actually follow</title><link href="https://bash-365.com/posts/2026/07/06/ai-acceptable-use-policy-your-team-will-follow/" rel="alternate" type="text/html" title="An AI acceptable-use policy your team will actually follow" /><published>2026-07-06T12:00:00+00:00</published><updated>2026-07-06T12:00:00+00:00</updated><id>https://bash-365.com/posts/2026/07/06/ai-acceptable-use-policy-your-team-will-follow</id><content type="html" xml:base="https://bash-365.com/posts/2026/07/06/ai-acceptable-use-policy-your-team-will-follow/"><![CDATA[<p>Your team is already using artificial intelligence (AI) at work. The only open question is whether they’re using it under rules you wrote or rules they improvised. At most 20–100-person businesses we see, the honest answer is the second one — and the policy that fixes it doesn’t need to be long. It needs to be followable.</p>

<h2 id="why-the-policy-youve-been-putting-off-matters-now">Why the policy you’ve been putting off matters now</h2>

<p>When we inventory software at small and medium businesses (SMBs), we typically find somewhere between a handful and a dozen AI tools in active use that nobody approved — free chatbot accounts, browser extensions, note-takers quietly sitting in on client calls. Each one is a place where company or client information now lives outside your control.</p>

<p>That exposure lands differently by industry. For a Denver law or accounting firm, a paralegal pasting a client matter into a personal chatbot account may have just stretched a confidentiality duty past its limit. For a dental clinic, patient details in a consumer AI tool are a Health Insurance Portability and Accountability Act (HIPAA) problem whether or not anything bad happens next. And on many free consumer tiers, the vendor’s terms allow your inputs to be used for model training — which means “just delete it” may not be an option later.</p>

<p>The instinct is to ban everything. That fails within a month: the staff who found these tools useful keep using them on personal devices, and now you carry the same exposure with zero visibility. The policy that works gives people a sanctioned way to do what they were already doing.</p>

<h2 id="the-three-lists-allow-ban-log">The three lists: allow, ban, log</h2>

<p>A workable SMB policy fits on one page and answers three questions.</p>

<p><strong>What’s allowed without asking.</strong> Drafting and editing internal documents and emails. Summarizing meeting notes and public information. Writing or reviewing code in company repositories. Brainstorming and research starting points. All of it on approved tools under company business accounts — business tiers generally offer admin controls and contract terms that exclude your data from training, which free tiers may not.</p>

<p><strong>What’s banned without written approval.</strong> Client-identifying or client-confidential information. Employee, payroll, or health records. Passwords, keys, and security configurations. Personal AI accounts for any company work. And the big one: sending AI output to a client, court, or regulator without a named human reviewing it first.</p>

<p><strong>What gets logged.</strong> Which tools are approved and who holds the accounts. Who reviewed AI-assisted work before it left the building. And for material decisions — pricing, hiring, anything that touches the books — the tool, the prompt, the output, and the reviewer.</p>

<p>If you want a framework behind those choices, the National Institute of Standards and Technology’s (NIST) <a href="https://www.nist.gov/itl/ai-risk-management-framework">AI Risk Management Framework</a> is the reference we use. In plain terms it asks for four things: govern (someone owns the policy), map (know where AI is actually used), measure (check the outputs), and manage (fix what the checks find). A one-page policy, a tool inventory, and a quarterly review cover all four at SMB scale.</p>

<h2 id="a-skeleton-you-can-copy">A skeleton you can copy</h2>

<p>Adapt the brackets, delete what doesn’t apply, and resist the urge to add legal boilerplate. A policy nobody reads is a policy nobody follows.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>AI acceptable-use policy — [Company name]
Effective [date] | Owner: [name, role] | Reviewed: quarterly

1. Approved tools
   - [Tool A] under the company business account only
   - [Tool B] under the company business account only
   - Anything else requires written approval from [owner/role]

2. Allowed without asking
   - Drafting and editing internal documents, emails, and marketing copy
   - Summarizing meeting notes and publicly available information
   - Writing or reviewing code in company repositories
   - Brainstorming, research starting points, and format conversion

3. Prohibited without written approval
   - Entering client-identifying or client-confidential information
   - Entering employee records, payroll data, or health information
   - Entering passwords, keys, or security configurations
   - Using personal AI accounts for any company work
   - Sending AI output to a client, court, or regulator without a
     named human reviewing and approving it first

4. Review and logging
   - AI-assisted work that leaves the company gets a human review;
     the reviewer is accountable for the content
   - [Owner/role] maintains the list of approved tools and accounts
   - Material decisions supported by AI (pricing, hiring, financial
     reporting) are logged: tool, prompt, output, reviewer, date

5. When in doubt
   - Ask [owner/role] before pasting. Asking is never a violation.
</code></pre></div></div>

<h2 id="how-the-rollout-plays-out">How the rollout plays out</h2>

<ol>
  <li><strong>Week 1 — inventory.</strong> Ask, don’t audit. A short survey (“what AI tools do you use, for what?”) with amnesty attached gets honest answers. This list is the real scope of your policy.</li>
  <li><strong>Week 2 — draft with the users.</strong> Write the three lists with the two or three heaviest users in the room. They’ll tell you which bans will be ignored and what approved alternative would make compliance the easy path.</li>
  <li><strong>Week 3 — roll out.</strong> Set up business accounts for the approved tools, walk the team through the one-pager in a single meeting, and name the owner people ask when unsure.</li>
  <li><strong>Quarterly — review.</strong> New tools appear, models change, and someone will request an exception. Fifteen minutes a quarter keeps the policy real.</li>
</ol>

<p>For most firms this size, the whole cycle is 2–4 weeks of part-time effort, and most of it is conversation rather than paperwork.</p>

<h2 id="watch-outs">Watch-outs</h2>

<ul>
  <li><strong>A ban without an alternative creates shadow use.</strong> If the policy says no and offers nothing, usage doesn’t stop — it goes underground, which is strictly worse.</li>
  <li><strong>A policy without logging can’t be enforced.</strong> If you can’t say which tools are approved and who reviewed what, the document is decoration.</li>
  <li><strong>Legal language kills adoption.</strong> Save the dense version for the employee handbook appendix if you must; the working copy is the one page people actually read.</li>
  <li><strong>Tools change faster than policies.</strong> The approved-tools list should be a living attachment the owner can update without re-ratifying the whole document.</li>
</ul>

<h2 id="next-step">Next step</h2>

<p>The policy is the paper half; the working half is the tool inventory, the business-account setup, and the guardrails that make the rules stick. That’s the first working session in [[AI solutions and intelligent automation]] — and it usually surfaces the riskiest paste before the policy is even final.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="corp" /><category term="ai" /><category term="ai-policy" /><category term="acceptable-use" /><category term="governance" /><category term="shadow-it" /><category term="denver-smb" /><category term="compliance" /><summary type="html"><![CDATA[What a 20-100-person business should allow, ban, and log when staff use AI, with a copy-paste policy skeleton you can adapt in an afternoon]]></summary></entry><entry><title type="html">What your cyber insurance renewal will ask about this year</title><link href="https://bash-365.com/posts/2026/07/06/cyber-insurance-renewal-questions/" rel="alternate" type="text/html" title="What your cyber insurance renewal will ask about this year" /><published>2026-07-06T12:00:00+00:00</published><updated>2026-07-06T12:00:00+00:00</updated><id>https://bash-365.com/posts/2026/07/06/cyber-insurance-renewal-questions</id><content type="html" xml:base="https://bash-365.com/posts/2026/07/06/cyber-insurance-renewal-questions/"><![CDATA[<p>The cyber insurance renewal application that lands in your inbox this year is longer than the one you signed last year, and the difference isn’t paperwork inflation. Carriers have learned exactly which missing controls turn a phishing email into a six-figure ransomware claim, and they now ask about each one directly.</p>

<h2 id="why-the-application-got-serious">Why the application got serious</h2>

<p>A few years ago, a small and medium business (SMB) cyber application was a page of yes/no questions. Today it’s a multi-page attestation, and your answers do three jobs at once: they set your premium, they set your exclusions, and they become evidence if you ever file a claim. An answer you guessed at — “sure, we have multi-factor on everything” — can void coverage at the exact moment you need it, because carriers investigate the attestation after an incident, not before.</p>

<p>Four controls carry most of the weight: multi-factor authentication, endpoint detection, backups, and offboarding. They also happen to track closely with the U.S. Cybersecurity and Infrastructure Security Agency’s (CISA) <a href="https://www.cisa.gov/cyber-essentials">Cyber Essentials guidance</a>, which is a useful free reference whether or not an insurer is asking.</p>

<h2 id="the-four-questions-that-move-the-number">The four questions that move the number</h2>

<h3 id="multi-factor-authentication-everywhere-that-matters">Multi-factor authentication, everywhere that matters</h3>

<p>Multi-factor authentication (MFA) means a second proof of identity beyond the password. The application will ask about it on email, on remote access, on administrator accounts, and increasingly on the backup console itself — because attackers who get in like to delete the backups first.</p>

<p>How it moves the number: for many carriers, “no” here isn’t a surcharge, it’s a declination or a ransomware exclusion. The good news is that MFA is usually the cheapest control on the list — on Microsoft 365 or Google Workspace it’s configuration work, not new spending.</p>

<h3 id="endpoint-detection-and-response-not-just-antivirus">Endpoint detection and response, not just antivirus</h3>

<p>Endpoint detection and response (EDR) watches behavior on laptops and servers — a process encrypting files at 2 a.m., a login from nowhere — and can isolate a machine automatically. That’s different from legacy antivirus, which checks files against a list of known bad ones. Applications now ask for the product by name and what percentage of devices it covers.</p>

<p>How it moves the number: weak answers here tend to show up less in the base premium and more in the fine print — higher retentions (your deductible) and lower ransomware sub-limits.</p>

<h3 id="backups-an-attacker-cant-reach">Backups an attacker can’t reach</h3>

<p>The application asks three things: do you back up the systems that matter, is at least one copy offline or immutable (meaning the same attacker who encrypted your network can’t encrypt it too), and have you actually tested a restore. A backup that lives on the same network with the same credentials as everything else counts as no. So does a backup that has never been restored — until it’s tested, it’s a hope, not a control.</p>

<p>How it moves the number: tested, separated backups shorten downtime, and downtime is most of what the insurer is pricing.</p>

<h3 id="offboarding-the-sleeper-question">Offboarding, the sleeper question</h3>

<p>How quickly are accounts disabled when someone leaves? Are there shared logins? Do former employees or contractors still have live credentials to email, remote access, or the accounting system? Stale accounts are one of the cheapest ways into a network, and carriers know it. For a multi-location retailer or a construction firm with seasonal field staff, this question deserves more attention than it usually gets.</p>

<h2 id="what-this-does-to-your-premium">What this does to your premium</h2>

<p>Ranges, because that’s what’s honest: SMB cyber premiums commonly run from the low four figures to the low five figures per year depending on revenue, industry, and limits. The four controls above largely decide which market you shop in. A documented yes on all four puts you in the standard market with competitive quotes. A no — or a “partially,” which carriers read as no — typically means some combination of higher retention, a ransomware sub-limit, an exclusion, or a push into surplus-market pricing that can run a multiple of standard. The premium is the visible cost; the exclusions are the expensive one.</p>

<h2 id="the-30-minute-self-audit">The 30-minute self-audit</h2>

<p>Do this before the broker’s deadline, not the night of.</p>

<ul>
  <li><strong>Minutes 0–5:</strong> Open your email admin console. Is MFA <em>enforced</em> for every user, or merely available? “Enabled but optional” is a no.</li>
  <li><strong>Minutes 5–10:</strong> Pick three computers, including one field or remote laptop. Confirm the EDR agent is installed, running, and reporting to a console someone actually watches.</li>
  <li><strong>Minutes 10–15:</strong> Find the most recent successful backup of the systems that would stop the business — accounting, job files, patient records. Note the date and where that copy lives.</li>
  <li><strong>Minutes 15–20:</strong> Find the date of the last <em>tested</em> restore. If the answer is never, that’s the answer.</li>
  <li><strong>Minutes 20–25:</strong> List the last three people who left the company. Check whether their accounts are disabled in email, remote access, and the accounting system.</li>
  <li><strong>Minutes 25–30:</strong> Write down every “partially” you just found. That list, in that order, is your pre-renewal to-do list.</li>
</ul>

<h2 id="watch-outs">Watch-outs</h2>

<ul>
  <li><strong>Never guess yes.</strong> An inaccurate attestation is grounds for a denied claim. If you’re not sure, the application is telling you what to go verify.</li>
  <li><strong>“Mostly deployed” is a no.</strong> Finish the rollout before you sign, not after.</li>
  <li><strong>Start 60–90 days out.</strong> Remediation takes weeks, and a quote requested the day before renewal locks in whatever your answers are that day.</li>
  <li><strong>Treat the application as a free assessment.</strong> It’s a carrier-funded list of the controls that most often decide whether an incident is an inconvenience or a catastrophe. The remote-access half of the list overlaps heavily with [[Remote work three years later: what actually worked]].</li>
</ul>

<h2 id="next-step">Next step</h2>

<p>If the self-audit produced more “partially” than you’d like, closing exactly these four gaps — and producing the documentation that lets you answer the application honestly — is standard scope for [[Managed IT services]]. Bring the application; it makes a very good project plan.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="corp" /><category term="cyber-insurance" /><category term="security" /><category term="mfa" /><category term="edr" /><category term="backups" /><category term="offboarding" /><category term="denver-smb" /><summary type="html"><![CDATA[The security questions on this year's cyber insurance application, how each answer moves your premium, and a 30-minute self-audit for Denver SMBs]]></summary></entry><entry><title type="html">The AI audit trail: log prompts like journal entries</title><link href="https://bash-365.com/posts/2026/07/06/ai-audit-trail-log-prompts-like-journal-entries/" rel="alternate" type="text/html" title="The AI audit trail: log prompts like journal entries" /><published>2026-07-06T12:00:00+00:00</published><updated>2026-07-06T12:00:00+00:00</updated><id>https://bash-365.com/posts/2026/07/06/ai-audit-trail-log-prompts-like-journal-entries</id><content type="html" xml:base="https://bash-365.com/posts/2026/07/06/ai-audit-trail-log-prompts-like-journal-entries/"><![CDATA[<p>No controller would post a journal entry with no date, no preparer, no source document, and no approval. Yet that’s exactly what an unlogged artificial intelligence (AI) step in a financial workflow is — an action inside your accounting process with none of the evidence attached.</p>

<h2 id="why-this-is-a-2026-problem">Why this is a 2026 problem</h2>

<p>AI is moving into the finance function at small and medium businesses (SMBs) through the side door: proposing general ledger (GL) codes for bank-feed transactions, drafting reconciliations, estimating accruals, writing collections emails. All useful. But finance has a century-old discipline for actions that touch the books — every journal entry carries who, what, when, a source document, and an approval — and AI actions in those same workflows carry none of that by default.</p>

<p>A vendor’s chat history is not an audit trail. It lives on the vendor’s retention schedule, in an account you may not control, in a format you can’t hand to an auditor. Meanwhile, financial records are typically retained for around seven years. If an AI-proposed categorization flows into an entry your auditor questions in year three, “the conversation expired” is not a working answer.</p>

<p>The control frameworks don’t carve out software, either. The Committee of Sponsoring Organizations of the Treadway Commission’s <a href="https://www.coso.org/guidance-on-ic">Internal Control — Integrated Framework</a> expects control activities and reliable information regardless of which actor performs a step. An AI proposing entries is a preparer. Preparers generate evidence.</p>

<h2 id="the-five-fields-mapped">The five fields, mapped</h2>

<p>Treat every AI touch of a financial workflow like a journal entry. The mapping is direct:</p>

<table>
  <thead>
    <tr>
      <th>Evidence on a journal entry</th>
      <th>Equivalent for an AI action</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Preparer</td>
      <td>Staff member who ran it, plus the model and version used</td>
    </tr>
    <tr>
      <td>Date</td>
      <td>Timestamp of the run</td>
    </tr>
    <tr>
      <td>Amounts and accounts</td>
      <td>The output, verbatim — proposed codes, numbers, or draft text</td>
    </tr>
    <tr>
      <td>Source document</td>
      <td>The exact input snapshot: the bank-feed export, the invoice batch</td>
    </tr>
    <tr>
      <td>Approval</td>
      <td>Named reviewer, their decision, and the date</td>
    </tr>
  </tbody>
</table>

<p>If you can produce those five fields for any AI-assisted step, you have a control. If you can’t, you have a very fast, very confident preparer working off the books.</p>

<h2 id="practical-logging-patterns">Practical logging patterns</h2>

<ul>
  <li><strong>Keep an AI register.</strong> One row per AI-assisted action, five fields per row. For a small finance team, a structured spreadsheet in a permission-controlled folder is a legitimate starting point; an append-only log table is better. Reference the row ID in the journal entry memo field (“prepared with AI, ref 2026-041”) so the trail runs in both directions — from the entry to the evidence and back.</li>
  <li><strong>Log at the workflow, not the chat.</strong> Capture the step where output enters the books — the bank-feed import, the journal entry batch — rather than the whole conversation. And always pair the output with its input snapshot; a prompt without its data can’t reproduce anything.</li>
  <li><strong>Match retention to the records.</strong> If the entry lives seven years, its evidence lives seven years. Export the register and snapshots to storage you control, on your schedule, not the vendor’s.</li>
  <li><strong>Separate the prompter from the approver.</strong> The person who runs the prompt isn’t the person who approves the posting — the same segregation of duties you already apply to manual entries. In a one-person finance function, use the same compensating control you would anywhere else: the owner reviews an exception report on a set cadence.</li>
  <li><strong>Version the prompts.</strong> A reusable categorization prompt is a control. Editing it mid-period is a control change: date it, note who approved it, and keep the prior version. Your auditor will care whether the logic changed between Q1 and Q3.</li>
</ul>

<h2 id="what-the-auditor-will-actually-ask">What the auditor will actually ask</h2>

<p>None of these questions are exotic — they’re the standard questions asked about any preparer. The only new part is that the preparer is software.</p>

<ul>
  <li>Show me every entry this period where AI was involved. (A register answers this in minutes; email archaeology takes weeks.)</li>
  <li>Who approved this output, and what did they compare it against?</li>
  <li>Can you reproduce this result — what data did the model see?</li>
  <li>What’s the exception process when the output is wrong, and can you show me one that was caught?</li>
  <li>Did the prompt change during the period? Who approved the change?</li>
</ul>

<p>Walking into fieldwork with a register that answers all five is the difference between AI reading as a well-controlled efficiency and AI reading as a scope expansion.</p>

<h2 id="how-it-plays-out">How it plays out</h2>

<p>For a typical SMB finance team, standing this up takes 2–4 weeks of part-time effort. Week one is scoping: list every place AI currently touches a financial workflow — the inventory is usually longer than the controller expects. Week two, define the register and the preparer/approver split for the highest-volume touchpoint, which is almost always bank-feed categorization. Weeks three and four, run it live, tune the exception process, and fold the rules into your broader AI use policy so the register’s scope and [[An AI acceptable-use policy your team will actually follow]] agree on what counts as touching the books.</p>

<h2 id="watch-outs">Watch-outs</h2>

<ul>
  <li><strong>Vendor chat history isn’t your evidence.</strong> Retention, export, and format are on the vendor’s terms. Assume it’s gone when you need it and log on your side.</li>
  <li><strong>Prompts without inputs are half a record.</strong> If you didn’t snapshot the data the model saw, you can’t reproduce the output — and a control you can’t demonstrate reads as a control you don’t have.</li>
  <li><strong>Never give the model posting rights.</strong> Outputs enter the books only through a human approval, and the register proves it. An AI that posts directly is an unreviewed preparer working at unlimited speed.</li>
  <li><strong>A partial register is worse than it looks.</strong> An AI action outside the register is this decade’s version of the side spreadsheet — invisible until it surfaces in fieldwork.</li>
</ul>

<h2 id="next-step">Next step</h2>

<p>This is the finance-literate side of AI adoption, and it’s where a dual accounting-and-systems background earns its keep. If AI is already drafting entries somewhere in your close, see our [[Finance tech]] — accounting systems and controls that hold up under audit.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="erp" /><category term="ai" /><category term="audit-trail" /><category term="internal-controls" /><category term="ai" /><category term="financial-close" /><category term="compliance" /><category term="segregation-of-duties" /><summary type="html"><![CDATA[If AI touches your financial workflows, its actions need the same evidence a journal entry carries: who, what, when, source, and approval]]></summary></entry><entry><title type="html">bashos: the new command-line operating system</title><link href="https://bash-365.com/posts/2026/07/06/bashos-the-new-command-line-operating-system/" rel="alternate" type="text/html" title="bashos: the new command-line operating system" /><published>2026-07-06T12:00:00+00:00</published><updated>2026-07-06T12:00:00+00:00</updated><id>https://bash-365.com/posts/2026/07/06/bashos-the-new-command-line-operating-system</id><content type="html" xml:base="https://bash-365.com/posts/2026/07/06/bashos-the-new-command-line-operating-system/"><![CDATA[<p>The terminal was supposed to be a museum piece. For two decades the industry tried to bury it under graphical user interfaces (GUIs), web consoles, and one-click dashboards, and for two decades the people who refused to leave it kept quietly out-shipping everyone else. Then, sometime in the last couple of years, the oldest interface in computing did something genuinely new: it grew a brain.</p>

<p>You can feel it the first time an agent reads your repo, forms a plan, runs your test suite, and hands you a diff — all from a request you typed where <code class="language-plaintext highlighter-rouge">ls</code> used to go. The shell stopped being a dumb pipe to the kernel and started behaving like an operating system in its own right: scheduling work, holding context, remembering what you told it last week, deciding which tools to call. I’ve started calling that composite <strong>bashos</strong> — not a product you install, but the posture the command line is taking on as artificial intelligence (AI) moves into it.</p>

<p>This is a field guide to that shift, written for people who already live in a terminal. The good news and the bad news are the same sentence: the thing coming for your job is also the best tool you’ve ever been handed. Learning to tell those two apart — and then to ride the one that’s left — is the whole new trade.</p>

<h2 id="from-prompts-to-an-operating-system">From prompts to an operating system</h2>

<p>A while back I argued that [[Prompts are the new command line]] — that the interface for getting work out of a machine had moved from syntax you memorize to intent you describe. That was the first half of the story. The second half is what the terminal does <em>after</em> it understands you.</p>

<p>An operating system, stripped to its job description, does a handful of things: it schedules work, manages memory, keeps track of context, brokers access to tools and devices, and enforces who is allowed to do what. Look at what a modern terminal agent does and the list is eerily familiar. It schedules a sequence of steps. It manages a context window like a working set, paging facts in and out. It remembers project conventions across sessions. It decides which command-line tool to reach for. It (should) ask before it touches anything dangerous. The shell has quietly acquired the responsibilities of a kernel — except the thing it schedules isn’t CPU time, it’s <em>intent</em>.</p>

<p>Make it concrete. Last year, “rotate the logging config across the fleet and confirm nothing broke” was a morning: Secure Shell (SSH) into box after box, edit files, grep for errors, eyeball the dashboards. The bashos version is one sentence of intent, after which an agent drafts the change, runs it against a staging box, diffs the before-and-after, and reports what moved — and you spend your morning deciding whether it was right, not typing it. The work didn’t vanish. It changed shape, from <em>doing</em> to <em>directing</em>.</p>

<p>This isn’t a metaphor I’m straining to make fit. As of mid-2026 the terminal-agent category is the fastest-moving corner of the developer-tools market, and the field is crowded: <a href="https://docs.anthropic.com/en/docs/claude-code/overview">Claude Code</a>, <a href="https://github.com/google-gemini/gemini-cli">Gemini CLI</a>, OpenAI’s <a href="https://developers.openai.com/codex/cli">Codex CLI</a>, OpenCode, Aider, Warp, Goose, and Amazon Q Developer CLI all run the same core loop — read your project, plan, execute in your shell, show you the result. Microsoft <a href="https://devblogs.microsoft.com/commandline/announcing-intelligent-terminal-version-0-1/">announced an <em>Intelligent Terminal</em></a> at Build 2026: a separate, opt-in build of Windows Terminal that pipes your shell context to whichever agent you bolt on — Copilot, Claude Code, Codex, Gemini — over a local Agent Client Protocol. And in the research world, projects like <a href="https://arxiv.org/abs/2403.16971">AIOS</a> have gone the literal distance and put a large language model where the kernel scheduler used to sit, complete with agent scheduling, memory management, and tool-access control.</p>

<p>Put those together and the trend line is clear. The command line is no longer just a way to <em>issue</em> instructions to an operating system. It’s becoming one.</p>

<h2 id="the-beast-is-your-best-frenemy">The beast is your best frenemy</h2>

<p>Here’s the part nobody on a vendor stage will say plainly: the same capability is both the threat and the tool, and there is no version of the future where you get one without the other.</p>

<p>Start with the threat, because pretending it isn’t there ages badly. A large share of traditional systems-administration work is exactly the kind of bounded, well-documented toil that an agent eats for breakfast. Provisioning, log triage, writing the same firewall rule for the hundredth time, turning a ticket into a three-line script: that used to be the moat. The moat is now a one-liner you hand to a process that doesn’t sleep, doesn’t get bored, and costs less per hour than your coffee. Technical skills date faster than they used to, and the specific keystrokes you’re proud of have a shorter shelf life than your laptop.</p>

<p>Now the other face of the same beast. The practitioners who actually fold AI into their workflow get real hours back — the ones that used to vanish into rote work — and demonstrable AI fluency is starting to separate otherwise-identical résumés. Engineering organizations running terminal agents at scale report shipping faster and reclaiming meaningful capacity. The threat and the tool are not two things. They are one thing, viewed from two sides of your own willingness to use it.</p>

<p>That’s what makes the competition your best friend-enemy. You cannot out-type the agent; it’s faster. You cannot out-remember it; it holds the whole repo in context. What you <em>can</em> do — the only durable move — is out-<em>judge</em> it: decide what’s worth doing, catch it when it’s confidently wrong, and own the outcome when it ships. Beating the beast and harnessing the beast turn out to be the same maneuver. You befriend it, you point it, and you stay the one holding the leash.</p>

<h2 id="the-job-mutates-from-operator-to-orchestrator">The job mutates: from operator to orchestrator</h2>

<p>The old loop was simple and human-bound: you think of a command, you type it, the machine runs it, you read the output, repeat. Every cycle went through your fingers. That ceiling — your typing speed and your recall — was the real constraint on a sysadmin’s throughput for forty years.</p>

<p>The new loop breaks the ceiling by taking your fingers out of the inner cycle. You state an intent. The agent drafts a plan. It executes in a sandbox. It shows you a diff or a dry-run. You review, correct, approve. You own the result. Notice what moved: you are no longer the <em>operator</em> of the machine. You are the <em>orchestrator</em> of something that operates the machine for you.</p>

<p>Which is to say you’ve been promoted into the kernel. Every responsibility an agent operating system has to handle, a human now handles one level up:</p>

<ul>
  <li><strong>Scheduling</strong> — which task goes to which agent, in what order, and what runs unattended overnight versus what waits for your eyes.</li>
  <li><strong>Context management</strong> — what the agent is allowed to know: the repo map, the runbook, the last incident, and pointedly <em>not</em> the customer’s personally identifiable information (PII).</li>
  <li><strong>Memory</strong> — the conventions, the architecture decisions, the “we tried that in 2024 and it broke” that you encode so it doesn’t relearn from scratch every session.</li>
  <li><strong>Tool brokering</strong> — which command-line tools, application programming interfaces (APIs), and integrations it may call, and which it may not.</li>
  <li><strong>Access control</strong> — least privilege, credentials, and the blast radius if it’s wrong.</li>
</ul>

<p>That is the literal job spec of an operating-system kernel, now running on you. The phrase that keeps rattling around my head is <em>operating systems built into them</em> — and it cuts both ways. The AI has an operating system built into it. And the professionals who thrive build an operating system into <em>themselves</em>: a disciplined internal model for how to dispatch, constrain, and verify a fleet of tireless, fallible workers. The trade isn’t disappearing. It’s moving up the stack, from the keyboard to the control room.</p>

<h2 id="how-to-harness-it-without-getting-bitten">How to harness it without getting bitten</h2>

<p>Enough theory. If bashos is the terrain, here is how to actually work it — the practices that separate people getting real leverage from people generating impressive-looking nonsense at scale.</p>

<h3 id="pick-one-agent-and-live-in-it">Pick one agent and live in it</h3>

<p>The instinct is to tool-hop — a new command-line interface (CLI) every week, chasing benchmarks. Resist it. The leverage comes from fluency, and fluency comes from repetition. Pick one capable terminal agent, point it at real work, and stay long enough to learn its failure modes: where it over-edits, where it invents a flag, where it needs a nudge. A tool you know cold beats a marginally better tool you’re always relearning.</p>

<h3 id="treat-context-as-the-new-config">Treat context as the new config</h3>

<p>The single biggest predictor of whether an agent helps or hurts is what it knows going in. Give it a map. Most agents now read a project file — <code class="language-plaintext highlighter-rouge">AGENTS.md</code>, <code class="language-plaintext highlighter-rouge">CLAUDE.md</code>, a <code class="language-plaintext highlighter-rouge">README</code>, a runbook — on startup, and that file is one of the most valuable things in your repo now. Write down the conventions, the architecture, the do-not-touch list, the commands to build and test. Context engineering has quietly overtaken prompt cleverness as the skill that matters: a plain prompt with great context beats a brilliant prompt with none.</p>

<h3 id="keep-a-hand-on-the-diff">Keep a hand on the diff</h3>

<p>The cardinal discipline is <em>plan, then diff, then commit</em>, with a human between each step on anything that matters. Run agents in a sandbox by default — Codex CLI, for one, isolates execution so it can install packages and run tests without touching your real environment. Dry-run destructive operations. Never let an agent push to production, drop a table, or <code class="language-plaintext highlighter-rouge">rm -rf</code> unattended. The cost of reading a diff is seconds; the cost of an unreviewed agent action against prod is your weekend, or your job.</p>

<h3 id="prefer-small-sharp-tools-over-heavyweight-everything-integrations">Prefer small, sharp tools over heavyweight everything-integrations</h3>

<p>There’s a quiet lesson coming out of teams running these agents in anger: feeding an agent a pile of giant, chatty integrations is often worse than letting it call small, composable command-line tools. The token cost alone is lopsided — a focused CLI call can be a couple of orders of magnitude cheaper than the equivalent fat-protocol round trip — and the behavior is more predictable. The Unix philosophy did not die when the AI showed up; it got a new customer. An agent that pipes <code class="language-plaintext highlighter-rouge">grep</code>, <code class="language-plaintext highlighter-rouge">jq</code>, <code class="language-plaintext highlighter-rouge">gh</code>, and <code class="language-plaintext highlighter-rouge">kubectl</code> together runs circles around one drowning in context it can’t use.</p>

<h3 id="codify-tribal-knowledge-into-reusable-assets">Codify tribal knowledge into reusable assets</h3>

<p>The senior engineer’s real value was never the keystrokes — it was the judgment encoded in their muscle memory and their home-directory aliases. Externalize it. Turn a runbook into a prompt. Turn a good prompt into a saved slash-command or a skill. Turn a recurring workflow into a scheduled agent that runs at 6am and leaves you a report. Each step converts something locked in one person’s head into an asset the whole team — and the agents — can run the same way every time. This is the modern Makefile, and it compounds.</p>

<h3 id="run-the-agent-like-an-over-eager-junior-with-root">Run the agent like an over-eager junior with root</h3>

<p>A useful mental model: the agent is a brilliant, fast, oddly confident junior who has somehow been handed root. You would not let that person operate without guardrails, and you delegate to them without abdicating to them. Give the agent its own identity, not your god-credentials — the platforms are moving toward exactly this, issuing agents their own scoped identity and audit trail. Least privilege, secrets out of prompts, log everything it does. In a world where everyone has the same models, your security posture is the differentiator that doesn’t commoditize.</p>

<h3 id="verify-like-you-dont-trust-it-because-you-shouldnt">Verify like you don’t trust it, because you shouldn’t</h3>

<p>An agent will hand you a wrong answer with the same cheerful confidence as a right one. The defense is to make verification cheap and automatic: tests, linters, type-checkers, and continuous integration (CI) are no longer hygiene — they’re the seatbelt that lets you drive fast. The teams getting the most out of agents are, paradoxically, the ones with the strictest automated checks, because strong verification is what makes it safe to delegate aggressively. Trust the beast exactly as far as your test suite can throw it.</p>

<h3 id="stay-fluent-in-the-fundamentals">Stay fluent in the fundamentals</h3>

<p>The great irony: AI in the terminal makes deep systems knowledge <em>more</em> valuable, not less. You cannot review a plan you don’t understand. You cannot catch a confidently wrong networking change if you don’t know how the network works. The professional who only knew the keystrokes is the one the agent replaces; the one who knows <em>why</em> becomes the indispensable reviewer of last resort. Keep reading man pages. Keep learning how the kernel, the filesystem, and the protocols actually behave. That knowledge is what you trade on now — your edge isn’t speed anymore, it’s correctness.</p>

<h2 id="what-to-do-this-week">What to do this week</h2>

<p>You don’t master this by reading about it. Pick one agent. Point it at a real repository you already understand — not a toy — and give it a genuine task: write the tests for a module, draft the runbook, refactor the ugly function. Write the <code class="language-plaintext highlighter-rouge">AGENTS.md</code> while you’re in there. Then read every diff before you accept it, and notice where it was brilliant and where it lied. Do that for a week and you’ll have a more honest sense of the beast than any blog post — including this one — can give you.</p>

<h2 id="field-notes-how-this-post-was-made">Field notes: how this post was made</h2>

<p>In the spirit of practicing what it preaches, this piece was built the way it describes working. The research — the current state of terminal agents, the Microsoft Intelligent Terminal, the shape of the workforce shift — was gathered by an AI agent fanning out across sources, then checked against more than one of them before any claim made it in. The structure was drafted, argued with, and rewritten. The judgment about what’s true, what’s overstated, and what’s worth saying stayed human. That’s the loop in miniature: let the beast do the fetching and the first draft; keep the deciding for yourself. A few things that made it work, and that generalize:</p>

<ul>
  <li><strong>Make it research before it writes.</strong> An agent that searches first and drafts second is dramatically more accurate than one riffing from memory. Force the order.</li>
  <li><strong>Give it the constraints up front.</strong> Audience, length, tone, the words to avoid — specified before the first draft, not patched in after.</li>
  <li><strong>Verify the numbers yourself.</strong> Every statistic an agent produces is a claim to check, not a fact to trust. The confident ones are the dangerous ones.</li>
</ul>

<p>That’s bashos in one paragraph: the machine got faster, and the part that’s still your job got more important.</p>

<h2 id="the-quest-restated">The quest, restated</h2>

<p>The quest was never to beat the beast. It was to become the kind of professional the beast makes <em>more</em> valuable instead of less — the one holding the leash, reading the diff, owning the call. bashos isn’t something you download. It’s something you become.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>supervise <span class="s2">"the beast"</span> &amp;
<span class="o">[</span>1] running
<span class="nv">$ </span><span class="c"># the prompt comes back. what you do with the freedom is the whole job.</span>
</code></pre></div></div>

<p>The competition really is your best friend-enemy. So make friends — and keep your hand on the leash.</p>

<p>And if you’re working out where terminal agents fit in your own shop — what to hand them, what to gate, and what to keep human — that’s the conversation behind our [[AI solutions and intelligent automation]].</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="tech" /><category term="ai" /><category term="ai" /><category term="agentic-ai" /><category term="cli" /><category term="terminal" /><category term="sysadmin" /><category term="devops" /><category term="automation" /><category term="bash" /><category term="future-of-work" /><summary type="html"><![CDATA[The command line is growing an operating system of its own, and the new IT trade turns on riding the beast instead of being automated out of a job by it]]></summary></entry><entry><title type="html">MCP for the back office</title><link href="https://bash-365.com/posts/2026/07/06/mcp-for-the-back-office/" rel="alternate" type="text/html" title="MCP for the back office" /><published>2026-07-06T12:00:00+00:00</published><updated>2026-07-06T12:00:00+00:00</updated><id>https://bash-365.com/posts/2026/07/06/mcp-for-the-back-office</id><content type="html" xml:base="https://bash-365.com/posts/2026/07/06/mcp-for-the-back-office/"><![CDATA[<p>The artificial intelligence (AI) assistant your team uses every day can’t see the systems your business actually runs on. So people copy out of the ticketing system, paste into the chat window, copy the answer back out, and reformat it. Copy-paste has quietly become your integration layer — and it’s slow, error-prone, and invisible to any audit.</p>

<h2 id="what-mcp-is-in-plain-english">What MCP is, in plain English</h2>

<p>The Model Context Protocol (MCP) is an open standard for connecting AI assistants to outside systems — file shares, databases, business applications. It was originally developed by Anthropic and is now openly published, with the <a href="https://modelcontextprotocol.io/">specification and documentation at modelcontextprotocol.io</a>.</p>

<p>The plainest way to think about it: before the Universal Serial Bus (USB), every device needed its own cable and its own port. MCP does for AI-to-system connections what USB did for peripherals — one standard plug instead of a custom cable per pairing.</p>

<p>There are two halves. The assistant side speaks the protocol. On the system side, a small adapter called an MCP server sits in front of each system and exposes a short menu of named operations — “search the policy folder,” “look up ticket status,” “read the AR export” — each with defined inputs and outputs. The assistant can only order off that menu. That constraint is the whole point.</p>

<h2 id="why-this-matters-for-an-smb-now">Why this matters for an SMB now</h2>

<p>Until recently, wiring an assistant into your file share or ticketing system meant a custom integration — the kind of project that gets quoted in weeks of development time and never gets funded at small and medium business (SMB) scale. A standard changes the economics. Adapters are small and reusable, vendors increasingly ship their own, and the work shifts from building plumbing to deciding permissions — which is where an owner’s attention belongs anyway. For the back-office wiring described below, that’s typically days of configuration and guardrail design rather than a custom development project.</p>

<h2 id="what-wed-wire-up-first">What we’d wire up first</h2>

<p>The value isn’t in exotic new systems. It’s in the ones you already run.</p>

<ul>
  <li><strong>The policy and procedure folder.</strong> A read-only MCP server over one specific file-share folder — employee handbook, safety procedures, standard operating documents. Staff ask “what’s our paid-time-off carryover rule” and get the answer with the source document cited. Nothing is ever written back.</li>
  <li><strong>The ticketing queue.</strong> The assistant reads tickets and drafts responses. An operations manager at a Denver construction firm asks “what’s still open on the Anderson job and who’s waiting on us” and gets a grounded summary instead of twenty minutes of clicking. Creating or closing tickets still goes through a person.</li>
  <li><strong>Accounting exports, not accounting.</strong> Point the assistant at the read-only exports your bookkeeper already pulls — the accounts receivable (AR) aging, the job-cost report — rather than at the ledger itself. It can summarize, flag anomalies, and draft the collections email. It cannot touch the books.</li>
</ul>

<h2 id="the-deterministic-foundation">The deterministic foundation</h2>

<p>Here’s the framing that keeps these projects safe. A language model is probabilistic: the same question can produce two different answers. Your general ledger, your job-costing system, and your ticket queue are deterministic: the same query returns the same record every time. Good architecture keeps the facts in the deterministic systems and the judgment in the model.</p>

<p>MCP enforces that split at the connector. Each tool the server exposes is a defined operation against a system of record — the assistant decides which tool to call and how to phrase the answer, but the number comes from the export and the ticket status comes from the queue. The model can’t invent an operation you didn’t expose. When a wrong answer would cost money, the thing that prevents it is a permission, not a clever prompt. This is the same trust discipline we laid out in [[Prompts are the new command line]], pushed down into the plumbing.</p>

<h2 id="guardrails-that-make-it-safe-to-plug-in">Guardrails that make it safe to plug in</h2>

<ul>
  <li><strong>Read-only first.</strong> Every connection starts as read-only. Write access is a separate, later decision made per operation.</li>
  <li><strong>Least-privilege service accounts.</strong> The MCP server connects with its own account scoped to exactly the folders and records it serves — never an administrator login.</li>
  <li><strong>Approval gates on anything that writes.</strong> Draft the ticket reply, propose the categorization — a person commits it.</li>
  <li><strong>Log every call.</strong> Who asked, which tool ran, with what parameters, and what came back. If AI touches financial workflows, that log needs real evidence discipline behind it.</li>
  <li><strong>Treat documents as untrusted input.</strong> A file in the share can contain text that tries to instruct the assistant — the reason gated writes and scoped permissions matter even for “just documents.”</li>
</ul>

<h2 id="how-it-plays-out">How it plays out</h2>

<ol>
  <li><strong>Phase 1 — read-only pilot (2–4 weeks).</strong> One system, usually the document folder. Includes the permissions review and the service-account setup. Your team’s job: name the folder, own the access decisions, and check the log weekly.</li>
  <li><strong>Phase 2 — drafting workflows (2–4 weeks).</strong> Ticket replies, collections emails, report summaries — assistant drafts, named humans approve.</li>
  <li><strong>Phase 3 — narrow writes, if earned.</strong> Only for operations where the log and the approval record from phase 2 proved out, and often never for anything that moves money.</li>
</ol>

<h2 id="watch-outs">Watch-outs</h2>

<ul>
  <li><strong>Vendor MCP servers aren’t automatically safe.</strong> Review the tool list an adapter exposes before connecting it, and disable operations you don’t need. “Official” doesn’t mean “scoped for you.”</li>
  <li><strong>Over-broad access defeats the design.</strong> A service account with admin rights turns a small mistake into a large one. The boring permissions work is the project.</li>
  <li><strong>Don’t start with the ledger.</strong> Start where wrong answers are cheap (documents) and move toward where they’re expensive (money) slowly — or not at all.</li>
  <li><strong>The demo-to-production gap.</strong> A connector that works in a ten-minute demo still needs the account scoping, logging, and approval design. Budget for that part; it’s most of the value.</li>
</ul>

<h2 id="next-step">Next step</h2>

<p>If there’s a copy-paste ritual in your back office that an assistant with two or three tightly scoped connections would end, that’s a well-bounded first project. See how we scope this kind of work in [[AI solutions and intelligent automation]].</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="tech" /><category term="ai" /><category term="mcp" /><category term="ai" /><category term="integration" /><category term="automation" /><category term="guardrails" /><category term="back-office" /><summary type="html"><![CDATA[What the Model Context Protocol means for small business back offices, and how to wire an AI assistant to your systems without losing control]]></summary></entry><entry><title type="html">Three is the magic number for automation that holds</title><link href="https://bash-365.com/posts/2026/06/20/three-is-the-magic-number/" rel="alternate" type="text/html" title="Three is the magic number for automation that holds" /><published>2026-06-20T11:00:00+00:00</published><updated>2026-06-20T11:00:00+00:00</updated><id>https://bash-365.com/posts/2026/06/20/three-is-the-magic-number</id><content type="html" xml:base="https://bash-365.com/posts/2026/06/20/three-is-the-magic-number/"><![CDATA[<blockquote>
  <p><em>One long brittle pipe—</em>
<em>a single stage coughs, the whole</em>
<em>morning runs to null.</em></p>
</blockquote>

<h2 id="the-straight-line-that-always-breaks">The straight line that always breaks</h2>

<p>Here is the automation everyone writes first. It is a straight line. It reads like a sentence and it dies like one.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>fetch | clean | enrich | score | route | notify | log | archive
</code></pre></div></div>

<p>Eight stages, seven pipes, one direction. It feels efficient because it <em>looks</em> efficient — no branching, no loops, no ceremony, just data falling downhill from left to right. You ship it. It runs beautifully for nine days. On the tenth, <code class="language-plaintext highlighter-rouge">enrich</code> times out against a third-party application programming interface (API) at 6 a.m., and because a Unix pipeline is only as alive as its narrowest dead stage, everything downstream of <code class="language-plaintext highlighter-rouge">enrich</code> receives an empty stream and dutifully processes nothing. <code class="language-plaintext highlighter-rouge">score</code> scores nothing. <code class="language-plaintext highlighter-rouge">route</code> routes nothing. <code class="language-plaintext highlighter-rouge">notify</code> notifies no one that nothing happened. By the time you look, the whole morning has run cleanly to <code class="language-plaintext highlighter-rouge">null</code> and reported success, because exit code 0 is the most dangerous lie in computing.</p>

<p>The straight line is brittle for a reason that has nothing to do with this particular API. It’s brittle because it has no way to <em>correct itself</em>. Data goes in one end and out the other, and nowhere in that geometry is there a path for the output to inform the input. A line is not a system. A line is a hope with stages.</p>

<p>The fix is not more stages. The fix is a shape. And the shape, it turns out, is almost always a triangle — because three is the magic number, and I mean that structurally, not mystically.</p>

<h2 id="three-is-the-magic-number">Three is the magic number</h2>

<blockquote>
  <p><em>Two legs and it tips,</em>
<em>four legs rock on uneven</em>
<em>floors—three sits dead still.</em></p>
</blockquote>

<p>Bob Dorough sang it in 1973 — <em>a man and a woman had a little baby, yes they did, there were three in the family, that’s a magic number</em> — and a generation learned multiplication from a cartoon that was, underneath the harmony, making a claim about structure. Three is not magic because it’s lucky. Three is magic because it’s the first number that holds.</p>

<p>Watch a three-legged stool on a stone floor. It never wobbles. It <em>cannot</em> wobble, because three points define exactly one plane, and any three points — however uneven the floor — sit flush against the single plane they define. A four-legged chair on the same floor rocks, because four points over-define the plane and the floor gets a vote. This is why surveyors use tripods, why your camera mount has three legs, why the milking stool has survived ten thousand years of furniture innovation unchanged. Three is the minimum that is also the maximum: the fewest supports that guarantee stability, and one more than that buys you nothing but a rock.</p>

<p>Now look at the triangle itself. In structural engineering the triangle is the only rigid polygon. Push on the corner of a square frame and it collapses into a rhombus — the angles give, the shape folds flat. Push on the corner of a triangle and nothing moves, because to change a triangle’s shape you’d have to change the <em>length</em> of a side, and the sides are members under tension and compression, not hinges. This is the entire reason bridges, cranes, roof trusses, transmission towers, and geodesic domes are built from triangles and not squares. The triangle distributes load to its corners and refuses to deform. Squares need diagonal bracing to survive — and a diagonal brace is just a confession that you wanted a triangle all along.</p>

<p>So when I tell you to build automation in threes, I am not invoking the rule of comedy or the three acts of a screenplay, though those rhyme. I am telling you that three is the smallest count of things that produces a structure instead of a pile. Two oscillates. Four wobbles. Three holds load.</p>

<h2 id="triangular-routes-are-the-most-efficient-loops">Triangular routes are the most efficient loops</h2>

<blockquote>
  <p><em>Wind dead on the bow—</em>
<em>you cannot sail straight; you tack</em>
<em>a triangle home.</em></p>
</blockquote>

<p>Here is the part that feels wrong until you’ve lived it: the most efficient route to a goal is often <em>not</em> the straight line at it.</p>

<p>A sailboat cannot sail directly into the wind. Point the bow at the thing you want when the thing you want is upwind, and you stop dead, sails luffing, going nowhere with great conviction. The efficient path to a point you cannot approach head-on is a triangle — you <em>tack</em>, sailing at an angle off the wind on one leg, then crossing to the opposite angle on the next, and the zigzag, which looks like a detour to anyone who’s never held a tiller, gets you there when the straight line gets you nothing. The triangle isn’t the scenic route. The triangle is the <em>only</em> route, and it is faster than the straight line that doesn’t move.</p>

<p>Mathematicians have a name for the deep version of this: the <strong>triangle inequality</strong>. For any three points, the direct side is never longer than the sum of the other two — <code class="language-plaintext highlighter-rouge">d(A,C) ≤ d(A,B) + d(B,C)</code>. It sounds like a tautology and it is the foundation of every routing optimizer ever written. It’s why a logistics planner reaches for a milk-run loop — depot to A to B to C and home — instead of driving out and back, out and back, out and back, three separate round trips from the depot. Each leg of the loop is never longer than the detour back through the depot would be, so the loop that touches three points in sequence beats the star that returns to center between each one. Triangulate the route and you stop paying for the trip home you take three times.</p>

<p>And the same geometry runs the wall you plugged this laptop into. The electrical grid does not deliver power as one wire doing its best. It delivers <strong>three-phase power</strong>: three alternating currents, each offset by 120 degrees — a third of a cycle — so that as one phase troughs, another peaks, and the sum delivers smooth, constant power with no dead spots. One phase pulses. Two phases still leave gaps. Three phases, spaced a third of a circle apart, hand the motor a continuous push and use less copper doing it. Every industrial motor on Earth runs on the discovery that three rotating things, evenly offset, never all rest at once.</p>

<p>That is what a good automation <em>loop</em> is. Not a line — a triangle of three nodes, each handing off to the next, the third feeding back to the first. You already know the shapes, because the disciplines that survived all converged on the same three-node loop and gave it different names:</p>

<ul>
  <li><strong>Sense → Decide → Act</strong>, then sense again. The control loop. The thermostat. The autopilot.</li>
  <li><strong>Red → Green → Refactor.</strong> Test-driven development is a triangle: write the failing test, make it pass, clean it up, write the next failing test. Three states, forever.</li>
  <li><strong>Extract → Transform → Load.</strong> The oldest data pipeline in the building, and it’s three stages because three is where it stabilizes.</li>
  <li><strong>Build → Measure → Learn.</strong> The whole of Lean, drawn as a loop with exactly three vertices.</li>
</ul>

<p>Notice what each of these has that the straight pipe didn’t: the third node points back at the first. <code class="language-plaintext highlighter-rouge">Act</code> changes the world that <code class="language-plaintext highlighter-rouge">Sense</code> reads. <code class="language-plaintext highlighter-rouge">Learn</code> rewrites what you <code class="language-plaintext highlighter-rouge">Build</code>. The triangle closes. That closure is the difference between a system that corrects itself and a sentence that runs to <code class="language-plaintext highlighter-rouge">null</code> at 6 a.m. and tells you it succeeded.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/usr/bin/env bash</span>
<span class="c"># Not a pipe. A loop with three vertices and a way home.</span>
<span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do
  </span><span class="nv">state</span><span class="o">=</span><span class="si">$(</span>sense<span class="si">)</span>                  <span class="c"># read the world</span>
  <span class="nv">action</span><span class="o">=</span><span class="si">$(</span>decide <span class="s2">"</span><span class="nv">$state</span><span class="s2">"</span><span class="si">)</span>       <span class="c"># choose, given the reading</span>
  apply <span class="s2">"</span><span class="nv">$action</span><span class="s2">"</span>                 <span class="c"># change the world</span>
  <span class="nb">sleep</span> <span class="s2">"</span><span class="nv">$INTERVAL</span><span class="s2">"</span>               <span class="c"># ...and sense the world you just changed</span>
<span class="k">done</span>
</code></pre></div></div>

<p>Two nodes — sense and act, no decide — and you get a relay that slams on and off, oscillating, because nothing mediates. Four nodes and you’ve added a vertex that someone will eventually have to explain to a new hire, and which will be the one that’s subtly out of phase. Three nodes hold load.</p>

<h2 id="chunk-the-goal-until-it-fits-in-one-slice">Chunk the goal until it fits in one slice</h2>

<blockquote>
  <p><em>Mind holds three at most—</em>
<em>break the mountain into three</em>
<em>stones you can lift now.</em></p>
</blockquote>

<p>A loop needs something to loop <em>over</em>, and this is where most automation dies a second, quieter death: the chunk is the wrong size.</p>

<p>Human working memory holds about three to four items at once — the number has shrunk in the literature since George Miller’s famous <a href="https://psychclassics.yorku.ca/Miller/">“seven, plus or minus two,”</a> and the honest modern figure is closer to four, often three under load. You do not overcome that ceiling by trying harder. You overcome it by <em>chunking</em>: grouping the raw material into a handful of units each small enough to hold whole. A phone number is ten digits you can’t retain and three chunks you can. A goal is the same. “Migrate the platform” is a mountain you cannot lift and cannot even see the top of. “Stand up the new database, dual-write to both for a week, cut reads over” is three stones, and you can pick up the first one today.</p>

<p>Machines have the same constraint wearing different clothes. In [[work &amp; play &amp; dev &amp;: job control for the sleep-deprived]], the lesson was that <code class="language-plaintext highlighter-rouge">dev</code> — the deep-work process — only makes progress in long uninterrupted blocks because it needs its whole working set resident and the cache hot; starve it of a contiguous slice and it produces nothing but guilt. Chunking is how you size work to the slice you can actually give it. Too big a chunk and the job can’t fit in one focused block, so it never completes and never checkpoints — it just thrashes, paging your attention to disk. Too small a chunk and you drown in overhead: the cost isn’t the work, it’s the context switch between the pieces, and a thousand tiny pieces means a thousand switches, each taxed.</p>

<p>The right chunk is the one you can carry through the loop exactly once — sense it, decide on it, act on it — before you have to set it down. And the elegant part, the part that closes the loop back to the triangle, is that the best chunking is <em>also</em> in threes. Break the goal into three. Break each of those into three. You descend a tree where every node has three children, and a tree of depth three already has twenty-seven leaves — enough granularity to schedule real work, shallow enough that you can still see the root from the leaf. Divide and conquer is the oldest algorithm we have, and it does not divide into eleven. It halves, or it thirds, and the thirds tend to map better onto the way a goal actually has a beginning, a middle, and a done.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># A goal you can't hold:</span>
deploy_the_whole_thing

<span class="c"># Chunked into a loop you can run, three at a time:</span>
<span class="k">for </span>stone <span class="k">in </span>stand_up_db dual_write cut_over<span class="p">;</span> <span class="k">do
  </span><span class="nv">result</span><span class="o">=</span><span class="si">$(</span>run_chunk <span class="s2">"</span><span class="nv">$stone</span><span class="s2">"</span><span class="si">)</span> <span class="o">||</span> <span class="o">{</span> rollback <span class="s2">"</span><span class="nv">$stone</span><span class="s2">"</span><span class="p">;</span> <span class="nb">break</span><span class="p">;</span> <span class="o">}</span>
  checkpoint <span class="s2">"</span><span class="nv">$stone</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$result</span><span class="s2">"</span>     <span class="c"># set it down before you pick up the next</span>
<span class="k">done</span>
</code></pre></div></div>

<p>Three stones. Each one small enough to lift, run through the loop once, and set down with a checkpoint — so that when <code class="language-plaintext highlighter-rouge">enrich</code> times out at 6 a.m., you lose one stone, not the morning.</p>

<h2 id="the-haiku-of-trade">The haiku of trade</h2>

<blockquote>
  <p><em>Five, seven, then five—</em>
<em>the small fast core reads the work,</em>
<em>routes it, steps aside.</em></p>
</blockquote>

<p>A haiku is three lines: five syllables, seven, five. It is the smallest poem that still completes a thought — short enough to hold whole in working memory, structured enough to have a shape, and built, of course, on three. The form survived a thousand years for the same reason the milking stool did: it is the minimum that holds. Every epigraph above this paragraph has been one, and you read each of them in a single slice without strain. That is not decoration. That is the chunk size of the human mind, set to verse.</p>

<p>There is a haiku of <em>trade</em>, too — the smallest complete transaction, the three-beat loop at the bottom of all commerce. A request comes in. A decision is made. A response goes out. Read the order, decide the fulfillment, ship the thing. Five-seven-five. Sense, decide, act. It is the same triangle wearing an apron, and for the whole history of business it had a human at the front of it: someone who read what came in, judged what it was, and routed it onward — the clerk, the dispatcher, the desk that triaged the morning’s mail into piles.</p>

<p>That front seat is now an artificial intelligence (AI) core. And here is the actual news, the thing under your brief: it is not the <em>biggest</em> model that takes the front. It’s the <em>smallest fast one</em>.</p>

<p>The pattern has a name in the trade now — the <strong>router</strong>, or the <strong>cascade</strong> — and the economics are the triangle inequality applied to compute. You do not send every request to the most powerful, most expensive model you own, any more than you’d drive a separate round trip from the depot for every single package. You put a small, fast, cheap model at the front — call it a <a href="https://docs.anthropic.com/en/docs/about-claude/models/overview">Haiku</a>, because that is precisely the role and, as it happens, the name of the model class built for it — and its whole job is the first beat of the loop: read the work, decide what it <em>is</em>, and route it. Most requests are simple, and the fast front-line core handles them whole and ships the answer in one slice. The genuinely hard ones — the cases where the triangle inequality says the detour through heavier compute actually pays — it escalates: hands them up to the heavyweight, the deep reasoner, or out to the human who’s the right one of the three to lift this particular stone. The fast core reads the work, routes it, and steps aside.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># The haiku of trade, with an AI core at the front.</span>
<span class="nv">classify</span><span class="o">=</span><span class="si">$(</span>haiku <span class="s2">"triage: </span><span class="nv">$request</span><span class="s2">"</span><span class="si">)</span>   <span class="c"># the fast front-line read — line one</span>
<span class="k">case</span> <span class="s2">"</span><span class="nv">$classify</span><span class="s2">"</span> <span class="k">in
  </span>simple<span class="p">)</span>  haiku  <span class="s2">"resolve: </span><span class="nv">$request</span><span class="s2">"</span> <span class="p">;;</span>   <span class="c"># ...handles it whole, ships it</span>
  hard<span class="p">)</span>    opus   <span class="s2">"resolve: </span><span class="nv">$request</span><span class="s2">"</span> <span class="p">;;</span>   <span class="c"># ...escalates to the heavyweight</span>
  human<span class="p">)</span>   queue_for_human <span class="s2">"</span><span class="nv">$request</span><span class="s2">"</span> <span class="p">;;</span>   <span class="c"># ...or routes to the right person</span>
<span class="k">esac</span>
<span class="c"># Front core fronts. All else follows what it sends.</span>
</code></pre></div></div>

<p>This is the inversion worth sitting with. The instinct is to put your most capable thing at the front of the operation — the senior engineer reads every ticket, the biggest model sees every prompt, the partner takes every call. It’s backwards, and it’s backwards for a reason you now have the geometry to see. The front of a loop is the <em>sensing</em> node, and sensing wants to be fast, cheap, and constant — three offset phases handing off a smooth signal, not one enormous pulse with dead spots between. You want the heavy capability held in reserve for the chunk that genuinely earns it, the way you hold <code class="language-plaintext highlighter-rouge">dev</code>’s deep block for the work that needs the whole cache hot. Put the fast core in front to triage and route, and the expensive capability stops being a bottleneck and becomes an escalation path. The Haiku takes the front. All else follows what it sends.</p>

<p>Which means the entire performance of the system now rides on a thing it never used to: the quality of the <em>routing</em>. When a small fast core sits at the front of the trade and everything downstream follows its read, a bad triage doesn’t fail loudly — it succeeds, confidently, at the wrong thing, and runs your morning to <code class="language-plaintext highlighter-rouge">null</code> while reporting exit 0. The clerk who mis-sorts the mail mis-sorts it fast and at scale. So the discipline shifts from <em>doing the work</em> to <em>checking the front</em> — auditing what the router escalates and what it doesn’t, watching the cases it waves through, keeping the human in the loop precisely at the vertex where the fast core decides who lifts the stone. The automation didn’t remove the judgment. It moved it to the front and made it the only thing that matters.</p>

<h2 id="exit-0">exit 0</h2>

<blockquote>
  <p><em>Haiku takes the front;</em>
<em>all else follows what it sends—</em>
<em>earn what comes behind.</em></p>
</blockquote>

<p>You will keep wanting to build the straight line. It reads so cleanly left to right, and the shape of a sentence is the shape of how we think a task should go: this, then this, then this, then done. But a line has no way home. It cannot correct what it got wrong because nothing in its geometry points backward, and so the first dead stage runs the rest of it to <code class="language-plaintext highlighter-rouge">null</code> and tells you it worked.</p>

<p>Three is the magic number, and the magic is structural. So, three things to take with you — because of course there are three:</p>

<p><strong>Build the loop, not the line.</strong> Three nodes, the third pointing back at the first. Sense, decide, act, and sense the world you just changed. The triangle is the only shape that holds load, and a loop that closes is the only automation that corrects itself.</p>

<p><strong>Chunk the goal until it fits in one slice.</strong> Three stones, each small enough to carry through the loop once and set down with a checkpoint. The right chunk size is the one that survives an interruption without losing the morning. When in doubt, third it.</p>

<p><strong>Put the fast core at the front.</strong> The haiku of trade now has an AI core reading the first line — small, fast, constant — routing most of the work whole and escalating only the chunk that earns the heavyweight or the human. That’s the efficient shape. But the front is now the whole game: all else follows what it sends, so audit the read, watch what it waves through, and make sure what comes behind is worth the follow.</p>

<p>The trade has a new clerk at the front desk, and it works in fives and sevens and fives, sorting the morning’s mail faster than any human ever could. For a Denver small or medium business (SMB), that is the difference between two analysts hand-triaging support tickets all morning and a small model routing most of them — often 70–85% — in seconds, freeing your people for the harder fraction that actually needs a human. That’s not the end of your job. That’s the start of a better one — minding the vertex where the fast core decides, because everything downstream is now following a haiku, and a haiku is only as good as its first line.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">exit </span>0   <span class="c"># this time, on purpose.</span>
</code></pre></div></div>

<hr />

<p><em>At BASH Consulting we build the automation that holds — loops that correct themselves, goals chunked to a size your team can actually carry, and AI cores placed where they make the system faster instead of where they make the demo flashier. If your pipeline is a straight line quietly running to <code class="language-plaintext highlighter-rouge">null</code> at 6 a.m., see how our [[Software development]] work puts the right shape — and the right model — at the front.</em></p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="muses" /><category term="automation" /><category term="workflow-design" /><category term="bash" /><category term="shell" /><category term="ai-engineering" /><category term="systems-thinking" /><category term="productivity" /><summary type="html"><![CDATA[Build automation that corrects itself — three-node loops, goals chunked to one slice, and a small fast AI routing the work at the front]]></summary></entry><entry><title type="html">work &amp;amp; play &amp;amp; dev &amp;amp;: job control for the sleep-deprived</title><link href="https://bash-365.com/posts/2026/06/20/work-play-dev-job-control/" rel="alternate" type="text/html" title="work &amp;amp; play &amp;amp; dev &amp;amp;: job control for the sleep-deprived" /><published>2026-06-20T10:00:00+00:00</published><updated>2026-06-20T10:00:00+00:00</updated><id>https://bash-365.com/posts/2026/06/20/work-play-dev-job-control</id><content type="html" xml:base="https://bash-365.com/posts/2026/06/20/work-play-dev-job-control/"><![CDATA[<h2 id="the-fantasy">The fantasy</h2>

<p>Every technologist has, at some low point, typed the equivalent of this into the universe and hit Enter:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>work &amp; play &amp; dev &amp;
<span class="o">[</span>1] 24601
<span class="o">[</span>2] 24602
<span class="o">[</span>3] 24603
<span class="err">$</span>
</code></pre></div></div>

<p>Three jobs. One ampersand each. All backgrounded, all running at once, prompt returned instantly, hands free. You lean back. You will earn a living, be a whole person, <em>and</em> finally ship the side project — concurrently, like a real machine. The dream of every overcommitted human is the same dream Unix sold us in 1973: throw an <code class="language-plaintext highlighter-rouge">&amp;</code> on the end and walk away.</p>

<p>Then <code class="language-plaintext highlighter-rouge">jobs</code> reports the truth.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">jobs</span>
<span class="o">[</span>1]   Running                 work &amp;
<span class="o">[</span>2]-  Stopped                 play
<span class="o">[</span>3]+  Running <span class="o">(</span>eating all memory<span class="o">)</span>  dev &amp;
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">play</code> is <strong>Stopped</strong>. It’s always Stopped. And somewhere off-screen, a fourth process you did not schedule is about to fork into the foreground and seize the terminal with a priority you are not authorized to override.</p>

<p>Let’s talk about job control.</p>

<h2 id="you-are-not-multi-core">You are not multi-core</h2>

<p>Here is the lie at the center of modern adult life: that you are running these things in parallel. You are not. You have one core. One. A single execution unit doing what single cores have always done — <em>time-slicing</em> so fast it produces the convincing illusion of simultaneity. The central processing unit (CPU) you bought in 2019 fakes multitasking by switching between processes thousands of times a second. You fake it by answering a Slack message at a playground.</p>

<p>The catch is the context switch. When a real CPU switches processes it has to dump the registers, flush part of the cache, load the next process’s state, and warm everything back up. It’s cheap in silicon and ruinous in wetware. When <em>you</em> switch from <code class="language-plaintext highlighter-rouge">dev</code> to <code class="language-plaintext highlighter-rouge">work</code> to “why is the toddler quiet,” you pay a context-switch tax measured not in nanoseconds but in the time it takes to remember what the function you were writing was supposed to return. The often-cited <a href="https://ics.uci.edu/~gmark/chi08-mark.pdf">University of California, Irvine study on workplace interruptions</a> found it takes people an average of about 23 minutes to return to an interrupted task. Your scheduler is running at maybe forty productive minutes an hour, and that’s on a good day with no SIGCHLD storm. (We’ll get to SIGCHLD.)</p>

<p>So the goal was never true parallelism. It was a tolerable time-slicing policy. Most of us are running the worst one available: switch on every interrupt, prioritize whatever screamed loudest, and let the lowest-priority job starve.</p>

<h2 id="jobs--l-meet-the-processes"><code class="language-plaintext highlighter-rouge">jobs -l</code>: meet the processes</h2>

<p>Before you can schedule three jobs you should know their resource profiles, because they do not behave alike.</p>

<p><strong><code class="language-plaintext highlighter-rouge">work</code></strong> is the daemon that pays for the electricity. It launches at boot whether you want it to or not, it has a habit of quietly raising its own priority — it starts the day at a polite <code class="language-plaintext highlighter-rouge">nice 0</code> and by 4 p.m. it’s somehow renice’d itself to <code class="language-plaintext highlighter-rouge">-5</code> and is preempting everything — and it <em>refuses to die on logout</em>. You close the laptop; <code class="language-plaintext highlighter-rouge">work</code> keeps running, because some prior version of you ran it with <code class="language-plaintext highlighter-rouge">nohup</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">nohup </span>work &amp;
work: ignoring input and appending output to <span class="s1">'nohup.out'</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">nohup</code> means “no hangup” — the process survives the terminal closing. That’s the technical name for the email that finds you at 9:47 p.m. It is immune to the hangup signal by design. You did this to yourself.</p>

<p><strong><code class="language-plaintext highlighter-rouge">dev</code></strong> is the fun one and the dangerous one. It’s your daemon, the thing you actually chose — the side project, the homelab, the repo nobody asked for. It is also a memory hog that only makes progress in long uninterrupted blocks. <code class="language-plaintext highlighter-rouge">dev</code> does not time-slice gracefully. You cannot give it ninety seconds between other tasks and expect output; it needs the cache hot and the whole working set resident, which is exactly the condition your life is least able to provide. When <code class="language-plaintext highlighter-rouge">dev</code> gets starved, it doesn’t crash — it just sits there as a process you keep meaning to bring back to the foreground, accruing guilt instead of commits.</p>

<p><strong><code class="language-plaintext highlighter-rouge">play</code></strong> is the process with the highest <code class="language-plaintext highlighter-rouge">nice</code> value in the system — <code class="language-plaintext highlighter-rouge">nice 19</code>, maximum niceness, which in the wonderful inversion of Unix means <em>lowest priority</em>. The nicer the process, the more readily it yields the CPU to everything else. <code class="language-plaintext highlighter-rouge">play</code> is so polite it never demands anything, which is precisely why it never runs. It is Stopped in your <code class="language-plaintext highlighter-rouge">jobs</code> list right now. Hold that thought, because the kernel has plans for it.</p>

<h2 id="fork">fork()</h2>

<p>Now the part nobody benchmarks for.</p>

<p>At some point you run <code class="language-plaintext highlighter-rouge">fork()</code>. The child process spins up. And every assumption your scheduler made goes out the window, because this is not a job you can background.</p>

<p>A forked child <em>inherits the parent’s environment</em>. This is true in Unix and it is true in the kitchen — the child gets a copy of your environment variables, your habits, your exported defaults, the way you say the word you wish you didn’t say. <code class="language-plaintext highlighter-rouge">export PATIENCE=thin</code> and watch it propagate.</p>

<p>But the real engineering problem is signals. In Unix, when a child process stops or terminates, the kernel sends the parent a signal called — and I want to be clear that I am not making this up — <strong><code class="language-plaintext highlighter-rouge">SIGCHLD</code></strong>. The child literally signals the parent. A toddler is a process that fires <code class="language-plaintext highlighter-rouge">SIGCHLD</code> at unpredictable intervals, demanding the parent immediately stop what it’s doing and handle it, and if the parent ignores the signal the child does not get cleaned up and the situation only gets worse. You end up writing a signal handler:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">trap</span> <span class="s1">'handle_snack_request'</span> SIGCHLD
</code></pre></div></div>

<p>And here is where job control breaks down completely:</p>

<ul>
  <li>You cannot run the child in the background. There is no <code class="language-plaintext highlighter-rouge">child &amp;</code>. It runs in the foreground by divine right and holds the terminal.</li>
  <li>You cannot <code class="language-plaintext highlighter-rouge">nohup</code> it. It does not survive you leaving the room; it follows you, signaling.</li>
  <li>You cannot <code class="language-plaintext highlighter-rouge">renice</code> it. It runs at <code class="language-plaintext highlighter-rouge">nice -20</code>, the maximum <em>negative</em> niceness, the highest priority the system allows — and on a real Unix box, setting a priority that aggressive requires root. You are not root. You filed the request and were denied. The toddler is root.</li>
  <li>And you absolutely cannot <code class="language-plaintext highlighter-rouge">kill -9</code> it. <code class="language-plaintext highlighter-rouge">kill -9</code> sends <code class="language-plaintext highlighter-rouge">SIGKILL</code>, the one signal a process cannot catch, block, or ignore — the nuclear option that always works. It does not work here. It is the one place in all of computing where <code class="language-plaintext highlighter-rouge">kill -9</code> is off the table, and thank goodness, but operationally: your hardest preemption tool has been removed from the rack.</li>
</ul>

<p>So now your <code class="language-plaintext highlighter-rouge">jobs</code> list looks like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">jobs</span>
<span class="o">[</span>1]   Running                 work &amp;
<span class="o">[</span>2]-  Stopped                 play
<span class="o">[</span>3]   Stopped                 dev
<span class="o">[</span>4]+  Running <span class="o">(</span>foreground<span class="o">)</span>    child   <span class="c"># nice -20, uninterruptible, owns the terminal</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">dev</code> got bumped to Stopped to make room. Of course it did.</p>

<h2 id="thrashing">Thrashing</h2>

<p>The naive response is to oversubscribe — keep all four “running,” background everything you can, and push harder. This is where you meet the two failure modes every overloaded system shares with every overloaded parent.</p>

<p>The first is <strong>swap</strong>. When a machine runs out of random-access memory (RAM) it starts paging memory out to disk, and if it’s doing that constantly it enters a state called <em>thrashing</em>: it spends so much effort moving pages back and forth that it does almost no actual work. The load average climbs, every process slows, and the box is technically up but accomplishing nothing. The human version is a Tuesday where you were busy for fourteen hours and shipped not one thing. You were thrashing. You were paging your attention to disk.</p>

<p>The second is the <strong>zombie</strong>. In Unix a zombie is a child process that has finished executing but whose parent never called <code class="language-plaintext highlighter-rouge">wait()</code> to read its exit status — so it lingers in the process table, done but un-reaped. Your life is full of zombies: the laundry that is technically washed but will never be folded, the pull request (PR) that’s approved but un-merged, the email you’ve read and mentally answered and not actually sent. Each one holds a slot. Enough un-reaped tasks and you run out of entries in the table, and the strange thing about zombies is you can’t <code class="language-plaintext highlighter-rouge">kill</code> them — they’re already dead. You have to reap them. The only fix is to finish the thing you already finished.</p>

<p>Meanwhile the parent, having not slept, has become a different kind of zombie entirely. The metaphor, like the laundry, does not fold cleanly.</p>

<h2 id="the-out-of-memory-oom-killer-always-comes-for-play">The out-of-memory (OOM) killer always comes for <code class="language-plaintext highlighter-rouge">play</code></h2>

<p>When memory pressure gets bad enough that swap can’t save it, the Linux kernel deploys its last resort: the <strong>OOM killer</strong>. It walks the process list, scores each one on a heuristic of how much it’d reclaim and how expendable it looks, and it <em>kills</em> the loser to keep the system alive. (If you want the gory scoring details, the kernel documents the <a href="https://docs.kernel.org/filesystems/proc.html"><code class="language-plaintext highlighter-rouge">oom_score_adj</code> tunable</a> in <code class="language-plaintext highlighter-rouge">/proc</code>.)</p>

<p>You already know which process it picks. It is never <code class="language-plaintext highlighter-rouge">work</code> — <code class="language-plaintext highlighter-rouge">work</code> pays for the RAM. It is never <code class="language-plaintext highlighter-rouge">child</code> — <code class="language-plaintext highlighter-rouge">child</code> is <code class="language-plaintext highlighter-rouge">nice -20</code> and untouchable. It is <code class="language-plaintext highlighter-rouge">dev</code>, and when <code class="language-plaintext highlighter-rouge">dev</code> is already Stopped, it is <code class="language-plaintext highlighter-rouge">play</code>. The OOM killer comes for <code class="language-plaintext highlighter-rouge">play</code> first, every single time, because <code class="language-plaintext highlighter-rouge">play</code> is the most expendable-looking thing in the table. It made itself maximally nice. It never demanded a slice. And so the system, doing exactly what it was configured to do, reaps the one process whose entire purpose was to make running the others bearable.</p>

<p>This is the actual bug. Not that there are too many jobs. That the scheduler is configured to starve the one job you cannot articulate a business case for.</p>

<h2 id="a-scheduler-that-actually-works">A scheduler that actually works</h2>

<p>The fix is not a productivity system. It’s a scheduling policy, and the good ones share a few properties.</p>

<p><strong>Stop pretending you’re parallel.</strong> <code class="language-plaintext highlighter-rouge">work &amp; play &amp; dev &amp;</code> is a lie your prompt tells you. You time-slice; the only question is whether you do it on purpose. Run one job in the foreground at a time and let it actually own the core. A focused hour on <code class="language-plaintext highlighter-rouge">dev</code> beats four hours of <code class="language-plaintext highlighter-rouge">dev</code> getting <code class="language-plaintext highlighter-rouge">SIGCHLD</code>‘d every nine minutes — because of the context-switch tax, those are not the same four hours.</p>

<p><strong><code class="language-plaintext highlighter-rouge">cron</code> the non-negotiables.</strong> The recurring, load-bearing events should not depend on you having spare attention to schedule them. Bedtime, the standing date, the gym, daycare pickup — these go in <code class="language-plaintext highlighter-rouge">crontab</code>, they fire on a timer, and they are not subject to renegotiation by whichever process is loudest at 5 p.m. A <code class="language-plaintext highlighter-rouge">cron</code> job doesn’t ask permission. That’s the feature.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># m  h  dom mon dow   command</span>
  0 18   <span class="k">*</span>   <span class="k">*</span>  1-5   run play <span class="nt">--no-laptop</span>   <span class="c"># weekdays, 6pm, non-negotiable</span>
  0 21   <span class="k">*</span>   <span class="k">*</span>   <span class="k">*</span>    reap_zombies           <span class="c"># fold the laundry you already washed</span>
</code></pre></div></div>

<p><strong>Batch your context switches.</strong> The cost isn’t the work, it’s the switch. Answer <code class="language-plaintext highlighter-rouge">work</code> in two or three deliberate windows, not continuously. Every time you <code class="language-plaintext highlighter-rouge">fg</code> and <code class="language-plaintext highlighter-rouge">bg</code> between jobs on a whim you pay the tax twice.</p>

<p><strong>Give <code class="language-plaintext highlighter-rouge">play</code> a guaranteed slice and protect it from the OOM killer.</strong> This is the whole point. The process with no business case is the one keeping the box alive; reserve CPU for it explicitly, the way you’d pin a critical service so the kernel can’t reap it. If <code class="language-plaintext highlighter-rouge">play</code> only runs on the scraps left after <code class="language-plaintext highlighter-rouge">work</code> and <code class="language-plaintext highlighter-rouge">dev</code> and <code class="language-plaintext highlighter-rouge">child</code>, it runs never, and then the other three degrade too, because they were always running on <code class="language-plaintext highlighter-rouge">play</code>’s output without admitting it.</p>

<p><strong>Let <code class="language-plaintext highlighter-rouge">dev</code> detach.</strong> This is what <code class="language-plaintext highlighter-rouge">tmux</code> and <code class="language-plaintext highlighter-rouge">screen</code> are <em>for</em> — a session that keeps running after you disconnect and is sitting exactly where you left it when you reattach. <code class="language-plaintext highlighter-rouge">dev</code> cannot demand a four-hour block from a life that contains a <code class="language-plaintext highlighter-rouge">nice -20</code> child. But it can survive interruption if you stop expecting to hold its whole state in your head and start letting the session hold it for you. Write the failing test before you stand up. Leave a <code class="language-plaintext highlighter-rouge">// TODO: you were here</code> at the cursor. Reattach, don’t restart.</p>

<p><strong>And handle the one interrupt that’s worth it synchronously.</strong> Here’s the turn. Everything above is about defending your jobs from interruption — except one. The <code class="language-plaintext highlighter-rouge">child</code> process is the single workload in the whole system where stopping the foreground task to handle the signal <em>is the task</em>. You will spend a number of years being interrupted by it, and the cruel math is that the interruptions feel like the thing keeping you from your real work, right up until the window closes and you understand they were the workload the entire time. <code class="language-plaintext highlighter-rouge">work</code> will fork new instances of itself forever. <code class="language-plaintext highlighter-rouge">dev</code> will still compile in a decade. The <code class="language-plaintext highlighter-rouge">nice -20</code> child downgrades to <code class="language-plaintext highlighter-rouge">nice 5</code>, then <code class="language-plaintext highlighter-rouge">nice 10</code>, then forks a process of its own and stops sending you <code class="language-plaintext highlighter-rouge">SIGCHLD</code> at all, and you would give a kidney to get one more.</p>

<p>So: protect <code class="language-plaintext highlighter-rouge">play</code>. Detach <code class="language-plaintext highlighter-rouge">dev</code>. <code class="language-plaintext highlighter-rouge">cron</code> the rest. And when the small process signals, handle it in the foreground, on purpose, without resentment. That’s not a failure of your scheduler. On a good day, that <em>is</em> the scheduler.</p>

<h2 id="the-same-bug-runs-your-business">The same bug runs your business</h2>

<p>Strip the parenting jokes and this is the scheduler running most small businesses we walk into. There’s a <code class="language-plaintext highlighter-rouge">work</code> daemon that renice’s itself to <code class="language-plaintext highlighter-rouge">-5</code> and eats every interrupt — the loudest customer, the squeakiest invoice, the integration that broke this morning. There’s a <code class="language-plaintext highlighter-rouge">dev</code> job that never gets a focused block, so the migration off QuickBooks or the reporting cleanup sits Stopped for two years. And there’s a <code class="language-plaintext highlighter-rouge">play</code> process — the strategic project with no screaming deadline and no obvious business case — that the OOM killer reaps first every quarter, because nobody pinned it.</p>

<p>The teams that ship the strategic work are not working more hours. They’ve just stopped letting the loudest process and the kernel’s lazy default decide the schedule. They give the load-bearing project a guaranteed slice, batch the interrupts into windows, and detach the long-running work so a single interruption doesn’t cost a full restart. The fix is a policy, not a person grinding harder.</p>

<h2 id="exit-0">exit 0</h2>

<p>You will not run <code class="language-plaintext highlighter-rouge">work &amp; play &amp; dev &amp;</code> in true parallel. No one does; the people who look like they do are just running a better time-slicing policy and not telling you about the swap. You have one core. You always did. The skill was never doing all of it at once — it was choosing, slice by slice, which process gets the core right now, and refusing to let the loudest one or the kernel’s lazy default decide for you.</p>

<p>Now if you’ll excuse me, job <code class="language-plaintext highlighter-rouge">[4]</code> is signaling, and this one I take in the foreground.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">fg</span> %4
</code></pre></div></div>

<hr />

<p><em>At BASH Consulting we spend our days untangling systems that are technically running but quietly thrashing — the strategic project that’s been Stopped for two years because the loud daemons eat every slice. If your team’s scheduler keeps reaping the wrong process, see [[Software development]].</em></p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="muses" /><category term="work-life-balance" /><category term="bash" /><category term="shell" /><category term="parenting" /><category term="productivity" /><category term="developer-life" /><summary type="html"><![CDATA[A field guide to running work, play, and dev as background jobs, and why fork()-ing a toddler rewrites your whole scheduler and your priorities]]></summary></entry><entry><title type="html">Frankenstein’s ERP: legacy fragmentation horror</title><link href="https://bash-365.com/news/erp/frankenstein-erp-legacy-fragmentation/" rel="alternate" type="text/html" title="Frankenstein’s ERP: legacy fragmentation horror" /><published>2026-01-31T05:58:24+00:00</published><updated>2026-01-31T05:58:24+00:00</updated><id>https://bash-365.com/news/erp/frankenstein-erp-legacy-fragmentation</id><content type="html" xml:base="https://bash-365.com/news/erp/frankenstein-erp-legacy-fragmentation/"><![CDATA[<h2 id="the-monster-you-already-own">The monster you already own</h2>

<p>It is 2026. Your Enterprise Resource Planning (ERP) vendor just announced an AI agent that predicts supply-chain disruptions before they happen. Bravo. Meanwhile, back in the fluorescent-lit cubicles of your IT team, someone is alt-tabbing between six different user interfaces from four different decades just to ship a pallet.</p>

<p>This is Frankenstein’s ERP: a stitched-together creature of modern browser tabs, multiple 2000s-era thick clients, and a character-based warehouse module that was probably coded during the Clinton administration. Vendors love to ignore it on the keynote slide. Your warehouse cannot.</p>

<h2 id="why-it-matters-now">Why it matters now</h2>

<p>The bill for this monster is real, and it is paid monthly. We routinely see mid-market manufacturers (roughly $200M-$500M in revenue) spending six figures a year on contractors just to keep the oldest pieces alive, plus a 28-35 person internal IT group whose actual job is “keep the green screens blinking.”</p>

<p>Two forces are squeezing these companies at once:</p>

<ul>
  <li><strong>Vendor cloud pressure.</strong> QAD and Infor are herding legacy customers toward subscription clouds with the urgency of a shepherd holding a cattle prod. QAD’s Adaptive ERP is now “agentic,” Infor ships AI agents faster than marketing can name them, and the “fixed-fee” migrations still somehow cost more than a house.</li>
  <li><strong>Vanishing talent.</strong> The people who can actually edit the old code are retiring or already gone, and nobody graduating in 2026 is learning a 4GL from 1984.</li>
</ul>

<p>Every day spent maintaining the creature is a day not spent on the modernization the business actually wants.</p>

<h2 id="a-guided-tour-of-ui-hell">A guided tour of UI hell</h2>

<p>Take a $400M manufacturer with plants in the US, Mexico, the UK, and India. Here is the daily dance a single user performs:</p>

<ol>
  <li>Open the <strong>modern web UI</strong> for basic order entry (limited features, naturally).</li>
  <li>Switch to <strong>.NET thick client #1</strong> for financials, because the web version still cannot run that one report.</li>
  <li>Fire up <strong>.NET thick client #2</strong>, the asset-management edition acquired in a 2008 buyout and never fully integrated.</li>
  <li>Launch <strong>.NET thick client #3</strong> for advanced production scheduling, a different acquisition from a different era.</li>
  <li>For the grand finale, boot the <strong>character-user-interface (CHUI) warehouse addon</strong> in a terminal emulator to print labels, scan receipts, and pray the session does not drop mid-shift.</li>
</ol>

<p>Six interfaces. Six different ways to hate your morning.</p>

<p>The CHUI module deserves its own spotlight. Built on <strong>Progress OpenEdge 4GL</strong> (a fourth-generation language so niche it is practically an endangered species), this green-screen relic runs the most mission-critical tasks on the floor: label printing, radio-frequency (RF) scanning, shipping, and receiving finished goods. One crash and the production line stops. It is the ERP equivalent of running your whole plant on a single 40-year-old steam valve while the vendor brags about its new electric turbine. The code is editable only through a fragile .NET-wrapped editor that crashes more often than the line it controls. This is not development. It is archaeology.</p>

<h2 id="the-offshore-4gl-wizard">The offshore 4GL wizard</h2>

<p>Need a bug fixed in the CHUI label routine? You had better hope your retainer with that one developer in India is still active. Progress 4GL expertise is concentrated halfway around the world, and the people who have it are guarded like nuclear codes.</p>

<p>A production receipt fails at 2 PM Colorado time. That is 1:30 AM in India. By the time the wizard wakes up, you have lost a shift, your inventory counts are wrong, and someone is hand-printing labels on a desktop printer like it is 1999. Lose that contractor to attrition and you are shopping in a talent market thinner than a quiet day in a warehouse.</p>

<h2 id="the-staffing-museum">The staffing museum</h2>

<p>For a $400M global manufacturer running this portfolio, the “bare minimum” IT organization looks less like a department and more like a museum staff for a live production system:</p>

<table>
  <thead>
    <tr>
      <th>Role category</th>
      <th>Headcount</th>
      <th>Unofficial job description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Leadership and governance</td>
      <td>3-4</td>
      <td>Professional apologizers for why nothing works together</td>
    </tr>
    <tr>
      <td>Functional support</td>
      <td>8-10</td>
      <td>Translators fluent in six UI dialects and one ancient 4GL incantation</td>
    </tr>
    <tr>
      <td>Technical maintenance</td>
      <td>6-8 plus contractors</td>
      <td>Includes 2-3 offshore wizards who can edit the sacred 4GL scrolls</td>
    </tr>
    <tr>
      <td>Infrastructure and endpoints</td>
      <td>5-7</td>
      <td>Keepers of terminal emulators, legacy printer queues, and Wi-Fi prayers</td>
    </tr>
    <tr>
      <td>Service desk</td>
      <td>6-8</td>
      <td>First responders to “why does this only work in the old client?”</td>
    </tr>
    <tr>
      <td><strong>Total</strong></td>
      <td><strong>28-35</strong></td>
      <td>A support group for technological trauma</td>
    </tr>
  </tbody>
</table>

<h2 id="the-watch-outs-where-escape-attempts-die">The watch-outs (where escape attempts die)</h2>

<p>Three things sink the “we’ll just migrate to the cloud” plan, and they are worth naming before you sign anything:</p>

<ul>
  <li><strong>The custom 4GL is not optional.</strong> Half a million lines of business logic live in that warehouse code. “Lift and shift” does not apply to a language the new platform cannot run. Someone has to re-implement it, and re-implementation is where seven-figure consulting bills are born.</li>
  <li><strong>The feature gaps are deliberate, not accidental.</strong> Some functions only exist in the old thick clients because the acquired products were never fully merged into the core. Confirm in writing that the target platform replicates every report and workflow your floor depends on, not just the ones in the demo.</li>
  <li><strong>Cutover risk lands on the warehouse, not the boardroom.</strong> A failed financial close is painful. A failed receiving cutover stops trucks at the dock. Sequence the migration so the CHUI-dependent processes move last, with a tested fallback and a parallel-run window of at least a few weeks.</li>
</ul>

<h2 id="what-we-would-actually-do">What we would actually do</h2>

<p>The honest answer is not “rip everything out next quarter.” It is to stop treating the creature as one unmovable mass and start dismantling it in priority order.</p>

<ol>
  <li><strong>Inventory the monster (2-4 weeks).</strong> Catalog every interface, every custom 4GL routine, every integration, and who actually depends on it. You cannot migrate what you have not mapped, and most shops have never written this down.</li>
  <li><strong>Rank by risk and pain.</strong> Score each component on business criticality, talent risk (how few people can support it), and contract cost. The CHUI warehouse module usually tops both lists.</li>
  <li><strong>Pick the battles worth fighting.</strong> Some pieces get replaced, some get a modern wrapper, and some get left alone because the math says so. This is a vendor-neutral decision, not whatever your incumbent is selling this quarter.</li>
  <li><strong>Phase the cutover.</strong> Move the lower-risk financial and order-entry pieces first to build the team’s confidence, then tackle the warehouse last with a real fallback plan.</li>
</ol>

<p>This is the work where a finance-literate, vendor-neutral outside team earns its keep: we can read the 4GL and the general ledger, and we have no incentive to push you onto any single platform.</p>

<h2 id="next-step">Next step</h2>

<p>You do not have to keep stocking Red Bull for 3 AM calls or perfecting your “it’s a feature, not a bug” smile. The first move is a clear-eyed map of what you own and what it is costing you. See our [[ERP consulting]] for SMB and mid-market manufacturers, and we will help you decide which parts of the monster are worth saving.</p>

<p>For the morbidly curious who want to understand what they are actually maintaining, the <a href="https://docs.progress.com/bundle/openedge-abl-reference/">Progress OpenEdge ABL reference documentation</a> is the primary source on the language keeping your warehouse alive.</p>

<p>In all of this, you are not the villain. You are the punchline, at least until you decide to write a new joke.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="erp" /><category term="erp" /><category term="legacy-systems" /><category term="qad" /><category term="infor" /><category term="manufacturing" /><category term="cloud-migration" /><category term="vendor-lock-in" /><category term="progress-4gl" /><summary type="html"><![CDATA[How fragmented legacy ERP systems trap manufacturers in a six-UI nightmare while vendors chase the cloud, and what to do about it]]></summary></entry><entry><title type="html">The circle of fix: how to escape the bug-rework loop</title><link href="https://bash-365.com/posts/2025/11/19/the-circle-of-fix/" rel="alternate" type="text/html" title="The circle of fix: how to escape the bug-rework loop" /><published>2025-11-19T10:00:00+00:00</published><updated>2025-11-19T10:00:00+00:00</updated><id>https://bash-365.com/posts/2025/11/19/the-circle-of-fix</id><content type="html" xml:base="https://bash-365.com/posts/2025/11/19/the-circle-of-fix/"><![CDATA[<h2 id="the-circle-of-fix">The circle of fix</h2>

<p><em>(Lights up. Frantic developers in cubicles, offshore support reps on video calls, and a giant spinning wheel labeled “Circle of Fix” looming over the stage like the Circle of Fifths but built entirely out of Jira tickets. The build has already failed. The orchestra swells.)</em></p>

<h3 id="opening-chorus-the-onshore-team">Opening chorus, the onshore team</h3>

<p><em>(to the tune of a rising perfect fifth, slightly off-key)</em></p>

<p>We shipped the code to Bangalore at night,
Thought we’d wake up to features shining bright!
But every patch they send comes back with flair,
A brand-new bug in every layer!</p>

<h3 id="offshore-support-choir-sunny-and-upbeat">Offshore support choir, sunny and upbeat</h3>

<p>Namaste, sir! Your ticket is resolved!
We fixed the null by making three evolve!
Please to be closing, rate us five out of five,
Regression suite? We keep that barely alive!</p>

<h3 id="the-circle-of-fix-full-company">The circle of fix, full company</h3>

<p><em>(sung in overlapping rounds, like a fugue that never resolves)</em></p>

<p>It goes C to G to D to A,
Then E fixes B which breaks yesterday!
Every dominant bug spawns a subdominant twin,
We modulate keys but the crashes stay in!</p>

<p>Round and round in the Circle of Fix,
Every perfect fifth costs us sixty-six!
We outsource the patch, they outsource the blame,
And the circle keeps turning, it’s always the same!</p>

<h3 id="verse-the-senior-dev-world-weary-baritone">Verse, the senior dev, world-weary baritone</h3>

<p>I once wrote clean code in the key of C-sharp,
Now I’m chasing stack traces through functions that warp.
They “refactored” my function to save fourteen lines,
Now it sings in twelve tones and summons daemons at runtime.</p>

<h3 id="verse-the-offshore-lead-cheerful-soprano">Verse, the offshore lead, cheerful soprano</h3>

<p>We work for one-fifth of your Silicon pay,
So you get one-fifth quality, fair trade, I’d say!
We fix it in React, then port it to Flask,
Then wrap it in COBOL because PM asked!</p>

<h3 id="bridge-the-product-manager-spoken-over-chaotic-jazz-chords">Bridge, the product manager, spoken over chaotic jazz chords</h3>

<p>“Scope is fixed, deadline is fixed, budget is fixed,
Everything else is flexible!”
<em>(Beat.)</em>
“Why is nothing working?!”</p>

<h3 id="final-chorus-everyone-descending-into-glorious-chaos">Final chorus, everyone, descending into glorious chaos</h3>

<p>Circle of Fix, modulating pain!
Every tritone resolved births a bug once again!
We’ll be here forever in seven-sharp hell,
Pushing hotfixes nightly while stakeholders yell!</p>

<p>Round and round in the Circle of Fix,
The bugs go marching four by four, hurrah? Hurrah?
We modulate up but the quality dips,
And the circle keeps spinning…
<em>(whisper)</em> …send it to the Philippines.</p>

<h3 id="blackout-on-a-single-unresolved-dominant-seventh-chord-a-lone-support-rep-from-the-dark">Blackout on a single unresolved dominant seventh chord. A lone support rep from the dark:</h3>

<p>“Have you tried turning it off and on again?”</p>

<hr />

<h2 id="why-the-wheel-keeps-spinning">Why the wheel keeps spinning</h2>

<p>It’s funny because it’s true. A lot of small and medium businesses (SMBs) in the Denver metro find themselves humming some version of this song: a feature gets built, it breaks something else, the fix gets handed to whoever is cheapest and available, and three weeks later you’re back where you started with a new bug and a thinner budget.</p>

<p>The wheel isn’t a vendor problem, an offshore problem, or a “bad developers” problem. It’s a process problem, and it gets very expensive very fast. The longer a defect survives before someone catches it, the more it costs to fix. A bug caught while you’re still writing the requirement is a five-minute conversation. The same bug caught in production, after a customer hits it, can cost orders of magnitude more to unwind, a relationship Barry Boehm documented decades ago and the U.S. National Institute of Standards and Technology (NIST) put hard numbers behind in its <a href="https://www.nist.gov/system/files/documents/director/planning/report02-3.pdf">report on the economic impacts of inadequate software testing</a>. Every loop around the Circle of Fix is you paying the late-stage price over and over.</p>

<h2 id="what-actually-breaks-the-cycle">What actually breaks the cycle</h2>

<p>You don’t escape the wheel by yelling at the support choir. You escape it by changing three things.</p>

<h3 id="1-write-requirements-someone-can-actually-build-to">1. Write requirements someone can actually build to</h3>

<p>Most “the offshore team broke it” stories are really “nobody wrote down what ‘it’ was.” When the spec is a Slack message and a hope, every developer fills the gaps with a different guess, and the gaps are where bugs live.</p>

<p>What “good enough” looks like for an SMB:</p>

<ul>
  <li>A one-page description of <em>what the user is trying to do</em>, not how to code it.</li>
  <li>Concrete acceptance criteria: “an invoice over 30 days past due shows in red on the dashboard,” not “improve the dashboard.”</li>
  <li>The edge cases you already know about (what happens with a $0 invoice, a refund, a deleted customer).</li>
</ul>

<p>You do not need a 40-page specification. You need enough that two different people would build the same thing.</p>

<h3 id="2-give-every-piece-of-work-one-owner">2. Give every piece of work one owner</h3>

<p>The song’s funniest line is the truest: “We outsource the patch, they outsource the blame.” When responsibility is shared across an onshore team, an offshore team, and a product manager, it is owned by no one, and the bug just keeps getting passed around the stage.</p>

<p>Pick one person, internal or external, who owns each feature end to end and is accountable for it working in production. Cheaper hands on the keyboard are fine. Diffuse ownership is what kills you.</p>

<h3 id="3-automate-the-regression-tests">3. Automate the regression tests</h3>

<p>The choir’s confession, “Regression suite? We keep that barely alive,” is the whole problem in one line. Without automated tests, every fix is a coin flip on whether it quietly breaks something that used to work. That’s the literal mechanism of the wheel: E fixes B which breaks yesterday.</p>

<p>A practical starting point:</p>

<ul>
  <li>Automated tests covering your handful of money paths (checkout, billing, the report leadership reads every Monday).</li>
  <li>A continuous integration check that runs those tests on every change <em>before</em> it merges.</li>
  <li>A short manual checklist for the things that are genuinely hard to automate.</li>
</ul>

<p>You don’t need 100% coverage on day one. You need a net under the 10 transactions that, if they broke, would cost you customers or a clean month-end close.</p>

<h2 id="how-it-plays-out">How it plays out</h2>

<p>For most SMB codebases, stepping off the wheel is a phased effort, not a rewrite:</p>

<ol>
  <li><strong>Stabilize (1-2 weeks):</strong> map the recurring bugs, find the two or three that keep coming back, and write tests that lock them down so they stop returning.</li>
  <li><strong>Add the net (2-4 weeks):</strong> stand up continuous integration and automate the money-path tests, so new changes get caught before they ship.</li>
  <li><strong>Tighten the intake (ongoing):</strong> adopt the lightweight requirements-and-ownership habit above for every new piece of work.</li>
</ol>

<p>What your team has to do: name the owner, tell us which transactions actually matter to the business, and resist the urge to skip testing the next time a deadline is “fixed, fixed, and fixed.”</p>

<h2 id="watch-outs">Watch-outs</h2>

<ul>
  <li><strong>Cheapest-bid whiplash.</strong> Rotating vendors for each fix guarantees nobody understands the system. Continuity beats hourly rate.</li>
  <li><strong>“We’ll add tests later.”</strong> Later never comes, and the wheel keeps spinning. The net pays for itself the first time it catches a regression before a customer does.</li>
  <li><strong>Refactors nobody asked for.</strong> Saving fourteen lines is not worth summoning daemons at runtime. Changes should trace back to a requirement and a test.</li>
</ul>

<h2 id="next-step">Next step</h2>

<p>If your software life feels like a chord that never resolves, the fix is process, not panic. Our [[Software development]] work helps Denver SMBs put requirements, ownership, and automated testing in place so the wheel finally stops turning.</p>

<p><em>Tired of singing the same chorus on loop? Let’s help you step off the wheel.</em></p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="muses" /><category term="software-development" /><category term="outsourcing" /><category term="agile" /><category term="devops-culture" /><category term="project-management" /><summary type="html"><![CDATA[How Denver SMBs break the fix-outsource-rework software loop with clear requirements, real ownership, and automated testing that catches regressions]]></summary></entry></feed>