I Thought I Understood Git. Then I Actually Learned the Difference Between Rebase and Merge.

Merge | Rebase graph

Merge | Rebase graph

I used git rebase long before I actually understood what it does.

I picked it up from a senior developer on my first team. The rule was simple: always rebase before you merge, keep the history clean.

I followed it. I typed the commands. I never questioned it.

Then one day I rebased the wrong branch and rewrote a week of shared history. That was the day I finally sat down and learned what I had been doing.

What merge actually does

git merge takes two branches and joins them. It creates a new commit — a merge commit — that has two parents: the tip of your branch and the tip of the branch you’re merging into.

# You're on feature/my-feature
git merge main

The result is a non-destructive operation. Nothing is rewritten. Your history grows a new node with two parents, and both lines of development are preserved exactly as they happened.

      A---B---C  feature
     /         
D---E---F---G---H  main (merge commit)

The upside — it’s safe.

The downside — if you merge large feature branches frequently, the commit history can become harder to read.

What rebase actually does

git rebase moves your commits. It takes the commits on your branch and replays them on top of another branch, one by one, as if you had started your work from there.

# You're on feature/my-feature
git rebase main
# Before
      A---B---C  feature
     /
D---E---F---G  main

# After
              A'--B'--C'  feature
             /
D---E---F---G  main

Notice the prime marks — A', B', C' are new commits. Same changes, new hashes. Rebase rewrites history.

The upside — your history looks linear and clean.

The downside — if anyone else has those original commits (A, B, C), their history now diverges from yours. This is what I did wrong.

The golden rule of rebase

Never rebase commits that exist outside your local repository.

If you’ve pushed a branch and someone else has pulled it, rebasing rewrites commits they already have. Their history diverges. Pulling becomes painful. You’ll see duplicate commits, conflicts that make no sense, and a very confused colleague.

The safe version — rebase only your local, unpushed work. Once commits are shared, merge.

# ✅ Safe — rebasing local work before pushing
git rebase main
git push origin feature/my-feature

# ❌ Dangerous — rebasing already-pushed commits
git push origin feature/my-feature  # already done
git rebase main                      # rewrites shared history
git push --force                     # now you've broken everyone else

Interactive rebase

Once you understand what rebase does, git rebase -i becomes one of the most useful tools in Git.

It lets you rewrite your local commit history before you share it — squash messy WIP commits into clean ones, reorder commits, edit commit messages, or split a large commit into smaller ones.

# Rewrite the last 3 commits interactively
git rebase -i HEAD~3

This opens an editor where you choose what to do with each commit:

pick a1b2c3 Add order validation
pick d4e5f6 fix typo
pick g7h8i9 WIP: still debugging

# Change to:
pick a1b2c3 Add order validation
squash d4e5f6 fix typo
drop g7h8i9 WIP: still debugging

The result — one clean commit instead of three messy ones.

This is what “clean history” actually means in practice — not avoiding merge commits, but being intentional about what you leave behind.

When to use which

Use merge when:

  • Integrating a completed feature branch into main or develop
  • Working on a shared branch where others have the same commits
  • You want to preserve the full context of when and how branches diverged
  • Merging release branches or hotfixes where an audit trail matters

Use rebase when:

  • Updating your local feature branch with changes from main before pushing
  • Cleaning up local commit history before opening a PR (with -i)
  • Keeping a long-lived feature branch up to date with the base branch
  • Working solo on a branch nobody else has touched

Never rebase:

  • Commits that have already been pushed and shared
  • main, develop, or any branch others are working from

The workflow that actually works

In practice, most teams land somewhere between “always merge” and “always rebase.” Here’s a workflow that’s served me well:

  1. Work locally on a feature branch
  2. Before opening a PR, git rebase -i to clean up commits
  3. git rebase main to update your branch with latest changes
  4. Push and open the PR
  5. Merge the PR into main (with a merge commit or squash merge — team’s call)

This gives you a clean PR history while preserving the merge commit that marks when a feature landed in main. Best of both approaches.

What I wish I had understood earlier

Rebase and merge aren’t competing philosophies. They’re different tools for different moments in a workflow.

Merge preserves history. Rebase rewrites it. Both are useful — but rewriting shared history is always a mistake.

I learned that the hard way. The senior developer who taught me to “always rebase” wasn’t wrong — he just assumed I understood what rebase does. I didn’t. I copied the command without understanding the consequence.

Now I understand both. And I still rebase — just never the wrong branch.

How does your team handle this? Do you have a strict rebase-only policy, or do you let developers decide? I’m curious whether the “clean history” argument is still winning in 2026. 👇

Why My Best Article Gets Zero ChatGPT Citations (And What I Changed)

`
Three thousand visitors a month. A handful of newsletter signups from it. Decent comments. By most measures, it’s performing well.

Then a few weeks ago I was testing something. I opened Perplexity and typed in the exact phrase my article targets. Five sources came back. Mine was not one of them. I tried ChatGPT with Browse enabled. Same thing. Gemini. Same.

I sat with that for a minute. A post I’ve spent more time on than anything else I’ve written is essentially invisible to the tools that a growing chunk of developers now use as their first stop for answers.

So I started digging.

What I assumed the problem was (I was wrong)

My first instinct was that the content was too long. AI tools prefer short, dense answers, right? So maybe 2,800 words was working against me.

Wrong. Some of the most-cited technical content I found when researching this runs 3,000+ words. Length is not the variable.

Then I thought maybe it was the topic. Too niche. Not enough people asking about it.

Also wrong. The keyword gets decent search volume and I confirmed other pages on the same topic were being cited regularly.

The actual problem was something I had not even thought to look at.

The thing I had never checked

I ran my article URL through GoForTool’s AI SEO Analyzer mostly out of curiosity. It does a full on-page audit specifically for AI search visibility, not traditional SEO.

The score came back: 27 out of 100.

I expected maybe 50, 60 tops given how much I’d thought about the writing. 27 was a genuine surprise. The fix list had five items and the top three explained everything.

Problem one: My answer appeared at word 380.

The tool flags something called answer position — how many words appear before your first direct response to the implied question in your title. Mine was at 380. The benchmark for regularly-cited content is under 120.

When I read back through my opening, I could see it immediately. I open with a story about when I first ran into the problem the article addresses. Good for human readers building context. Terrible for an AI trying to extract a clean answer quickly. LLMs weight earlier content more heavily. If your answer is buried, they will find someone else’s answer that isn’t.

Problem two: I was using vague language throughout.

I had phrases like “modern JavaScript frameworks,” “popular bundlers,” “leading cloud providers.” None of those are entities an AI can pin to anything specific. ChatGPT-4o, Perplexity, and Gemini all understand named things. They understand “Vite 5.0,” “esbuild,” “Cloudflare Workers.” They do not understand “popular bundlers.” That phrase could mean anything.

Problem three: PerplexityBot was blocked on my site.

This one stung. A robots.txt rule I had added over a year ago was blocking PerplexityBot from crawling my content. I had not thought about it since. Every article I have written in the last 14 months has been invisible to Perplexity not because of anything wrong with the writing but because of a single line in a config file.

The fixes and how long they took

Fix one: Rewrote the opening paragraph. I moved the story to a later section and put a direct, specific answer up front. Something like: “Optimising Vite build performance for large React apps requires four configuration changes: chunk splitting with manualChunks, enabling build.minify with esbuild, configuring rollupOptions for tree shaking, and using vite-plugin-inspect to identify bottlenecks.” That is now the first thing anyone reads. 20 minutes.

Fix two: Named everything specifically. Went through the article and replaced every vague reference. “Popular bundlers” became “Vite 5, Rollup 4, and esbuild.” “Modern browsers” became “Chrome 120, Firefox 121, and Safari 17.” This took about 15 minutes and made the article substantially more useful for human readers too, not just AI.

Fix three: Fixed robots.txt. Added explicit Allow rules for GPTBot, PerplexityBot, ClaudeBot, and Google-Extended. Five minutes.

Fix four: Added FAQPage schema. Three question-answer pairs embedded in a JSON-LD script tag in the page head. This one I did through GoForTool because it generates the schema for you based on your content — I did not have to write JSON by hand. 10 minutes.

Total time: about 50 minutes across an evening.

What happened after

I gave it two weeks before checking anything.

The article now shows up in Perplexity responses for two related queries. Not my exact target phrase yet, but two adjacent ones. ChatGPT Browse cited it once in a test I ran last week — showed up as a source in an answer about Vite configuration.

My GEO score went from 27 to 71 after the changes. GoForTool lets you re-scan after edits so you can confirm things landed before moving on.

The Google traffic has not changed. Same 3,000 visitors, same position. But now the content is doing a second job it wasn’t doing before.

The robots.txt thing is probably affecting you too

I want to highlight this specifically because I think it is the most commonly missed issue and the fastest to fix.

A lot of developers added security-focused robots.txt rules over the past few years in response to scrapers and bots. Totally reasonable. But AI crawlers use user agents that many of those rules accidentally caught.

Check yours right now. Go to yourdomain.com/robots.txt. Look for any Disallow rules that apply to wildcards or to specific bots you may not have evaluated recently. Then add explicit Allow entries for the crawlers you actually want:

`robot_framework
User-agent: GPTBot
Allow: /

User-agent: PerplexityBot
Allow: /

User-agent: ClaudeBot
Allow: /

User-agent: Google-Extended
Allow: /
`

Seriously, do this before anything else. If you are blocked, nothing else you do matters.

Running this audit on your own content

The manual version of what I described takes 15 to 20 minutes per URL: read through your opening for answer position, count your named entities, inspect your schema, check robots.txt. Doable, but slow if you have a lot of content.

The automated version through GoForTool’s AI SEO Analyzer takes about 90 seconds per URL and catches things I was missing manually. It also generates the schema for you rather than making you write it from scratch.

I’m now running it on every article before publishing. The pre-publish check has become part of my workflow the same way running a linter before committing has become automatic — you just do it.

What does your GEO score come back as? Drop it in the comments with a rough description of your content type. Happy to suggest what to fix first based on the number.

seo #ai #webdev #javascript`

Multi-Agent Systems on AWS Lambda with Durable Functions

In my previous post on multi-agent systems, I built a purchasing coordinator where a coordinator agent routes requests to specialist agents based on RISEN prompt contracts. RISEN structures system prompts into five components: Role, Instructions, Steps, Expectation, and Narrowing. In a multi-agent system, the Steps section encodes the routing logic and the Narrowing section prevents agents from doing each other’s work. A laptop triggers Price Research and Delivery. A used car triggers all five specialists. The routing logic lives in the prompts, not in code. It works, but it runs in a single process. No fault isolation, no independent scaling, no durability. If the process crashes halfway through specialist consultations, you start over.

The durable functions post solved the durability problem for a support ticket triage workflow: checkpoint each step, suspend for human review, resume where you left off. But that was a single-agent workflow.

This post combines the two. The same purchasing coordinator, deployed to AWS Lambda, with each specialist as its own Lambda function. The coordinator is a durable function that checkpoints every specialist call. If it’s interrupted after consulting three of five specialists, it resumes from the fourth. When a high-value purchase needs human approval, the function suspends, compute charges stop, and it picks up exactly where it left off when the approver responds.

The two SDKs have distinct roles. The Strands Agents SDK handles the AI reasoning: the planning agent decides which specialists to call, and the synthesis agent produces the recommendation. The durable execution SDK handles the infrastructure: checkpointing, parallel dispatch, suspension, and replay. The coordinator uses both. The specialists only use Strands.

The complete source code is on GitHub: github.com/gunnargrosch/durable-multi-agent-purchasing.

What Changes from the In-Process Demo

The multi-agent post used tool() callbacks for specialist dispatch: each specialist was defined as a Strands tool, and the coordinator agent called them as functions within the same process. That’s the simplest possible architecture, and it’s fine for development. Here’s what changes when you deploy:

In-Process (previous post) Lambda + Durable (this post)
Specialist invocation tool() callback, in-process context.invoke(), separate Lambda function
Execution model Sequential (one specialist at a time) Parallel via context.parallel()
Fault tolerance None. Process crash = restart Checkpointed. Resume from last completed step
Scaling Single process Each specialist scales independently
Human-in-the-loop Not supported waitForCallback() with zero-cost suspension
IAM isolation Shared process permissions Per-specialist least-privilege policies
Routing visibility Real-time hook as each tool is called Checkpointed plan step with routing summary
Infrastructure npm start SAM template, 6 Lambda functions

The RISEN prompts carry over with only minor adjustments. The coordinator’s prompt splits into two phases (plan and synthesis) instead of a single invocation, because the durable function needs to checkpoint the plan before dispatching specialists. The specialist prompts are unchanged.

How It Works

If you haven’t read the durable functions post, here’s the key mental model: durable functions use checkpoint and replay. Your handler re-executes from the top on every resume, but completed steps return their cached results instantly without re-executing. New work picks up from where it left off. The SDK manages this transparently. You write sequential code and the infrastructure handles the rest.

[CoordinatorFunction] — Lambda Durable Function (Sonnet, 1536MB)
  ├→ context.step('plan')            → Planning agent selects specialists
  ├→ context.parallel('specialists') → Runs selected specialists concurrently:
  │     ├─ context.invoke(PriceResearchFunction)  (Haiku)   ← always
  │     ├─ context.invoke(FinancingFunction)       (Haiku)   ← if value > $5K
  │     ├─ context.invoke(DeliveryFunction)        (Haiku)   ← if physical product
  │     ├─ context.invoke(RiskAssessmentFunction)  (Sonnet)  ← if value > $10K / used
  │     └─ context.invoke(ContractReviewFunction)  (Haiku)   ← if subscription/lease
  ├→ context.step('synthesize')      → Synthesis agent combines findings
  └→ context.waitForCallback()       → Human approval (when requireApproval=true)

Three things to notice:

  1. context.invoke() is a new primitive not covered in the durable functions post. It’s the SDK’s built-in method for calling other Lambda functions. It checkpoints the result automatically and suspends the coordinator while waiting, so you don’t pay for compute during specialist execution. Despite the SDK’s API reference describing it as invoking “another durable function,” context.invoke() works with any Lambda function. The specialists here are standard functions without DurableConfig.
  2. context.step() wraps the planning and synthesis phases with retry strategies, just like the Bedrock calls in the support triage demo. Each step checkpoints its result. On replay, it returns the cached result without re-executing.
  3. context.parallel() wraps the specialist invocations so they run concurrently, each independently checkpointed. If specialist 3 of 5 fails, the other four results are preserved.

The SAM Template

Here are the key parts of the coordinator’s definition in the SAM template. The template defines 6 functions total: the coordinator plus 5 specialists that follow the same pattern.

CoordinatorFunction:
  Type: AWS::Serverless::Function
  Properties:
    Handler: coordinator.handler
    MemorySize: 1536
    Timeout: 300
    Environment:
      Variables:
        COORDINATOR_MODEL_ID: !Sub global.${CoordinatorModelId}
        PRICE_RESEARCH_FUNCTION: !Ref PriceResearchFunction
        # ... remaining specialist function references
    Policies:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy
      - Statement:
          - Effect: Allow
            Action:
              - bedrock:InvokeModel
              - bedrock:InvokeModelWithResponseStream
            Resource: # Bedrock model + inference profile ARNs
          - Effect: Allow
            Action: lambda:InvokeFunction
            Resource:
              - !GetAtt PriceResearchFunction.Arn
              - !GetAtt FinancingFunction.Arn
              - !GetAtt DeliveryFunction.Arn
              - !GetAtt RiskAssessmentFunction.Arn
              - !GetAtt ContractReviewFunction.Arn
    DurableConfig:
      ExecutionTimeout: 86400
      RetentionPeriodInDays: 7
    AutoPublishAlias: live

A few things to note:

  • No API Gateway, no function URLs, no public endpoints. The multi-agent post previewed two deployment options: Lambda with HTTP endpoints, or AgentCore containers. This implementation goes a simpler route. context.invoke() calls specialists directly via the Lambda API. The coordinator’s IAM policy grants lambda:InvokeFunction on each specialist ARN. Specialists are unreachable from outside the coordinator’s execution role.
  • Shared handler, different prompts. All five specialists use the same specialist.ts handler. The PROMPT_NAME environment variable selects which RISEN prompt to load. This keeps the template repetitive but predictable: each specialist block differs only in its name, PROMPT_NAME, and model ID.
  • Per-specialist model selection. Risk Assessment gets AdvancedSpecialistModelId (Sonnet) for stronger reasoning. The other four get SpecialistModelId (Haiku). Same pattern as the in-process demo, now enforced at the infrastructure level.
  • DurableConfig with 24-hour execution timeout. The used car scenario with human approval can sit overnight. Each individual invocation is bounded by Timeout: 300 (the coordinator’s per-replay limit). ExecutionTimeout: 86400 is the outer wall-clock limit across all replays and suspensions.
  • InvokeModelWithResponseStream is included because the Strands SDK’s BedrockModel may use streaming internally for token generation. Without it, the coordinator would get AccessDeniedException on agent invocations.
  • ESM build. The coordinator uses ESM (Format: esm) with esbuild. The full template includes a Banner that injects a createRequire shim because some dependencies expect CommonJS require. This is a known pattern for ESM Lambda functions with mixed dependencies. See the repo’s template.yaml for the complete Metadata block.

The Coordinator Handler

The coordinator is the only durable function. Here’s the handler skeleton showing the four durable phases. The full source includes types, the specialist registry, retry strategies, and routing summary logic.

export const handler = withDurableExecution(async (
  event: CoordinatorEvent,
  context: DurableContext
): Promise<Record<string, unknown>> => {

  // Phase 1: Plan — agent decides which specialists to consult
  const plan = await context.step<AnalysisPlan>('plan', async () => {
    let capturedPlan: AnalysisPlan | null = null
    const planTool = tool({
      name: 'create_analysis_plan',
      inputSchema: z.object({
        specialists: z.array(z.object({
          name: z.enum(['price-research', 'financing', 'delivery',
                        'risk-assessment', 'contract-review']),
          prompt: z.string(),
        })),
      }),
      callback: async (input) => { capturedPlan = input; return 'Plan created.' },
    })
    const agent = new Agent({ model, systemPrompt: planPrompt, tools: [planTool], printer: false })
    await agent.invoke(event.request)
    if (!capturedPlan) throw new Error('Planning agent did not call create_analysis_plan')
    return capturedPlan
  }, { retryStrategy: bedrockRetry })

  // Phase 2: Consult specialists in parallel via context.invoke()
  const results = await context.parallel<{ name: string; response: string }>('specialists',
    plan.specialists.map((spec) => ({
      name: spec.name,
      func: async (ctx) => {
        try {
          const specialist = SPECIALISTS[spec.name]
          if (!specialist) {
            throw new Error(`Unknown specialist: ${spec.name}`)
          }
          const result = await ctx.invoke<{ prompt: string }, { response: string }>(
            spec.name, specialist.functionName, { prompt: spec.prompt }
          )
          return { name: spec.name, response: result.response }
        } catch (err) {
          const display = SPECIALISTS[spec.name]?.display ?? spec.name
          return { name: spec.name, response: `[${display} unavailable: ${err instanceof Error ? err.message : 'unknown error'}]` }
        }
      },
    }))
  )

  // Phase 3: Synthesize specialist findings into a recommendation
  const response = await context.step('synthesize', async () => {
    // Build findings from checkpointed parallel results, invoke synthesis agent
    // (see full source for details)
  }, { retryStrategy: bedrockRetry })

  // Phase 4: Human approval (optional)
  if (event.requireApproval) {
    const approval = await context.waitForCallback<ApprovalPayload>(
      'approval',
      async (callbackId) => { context.logger.info('approval_callback_created', { callbackId }) },
      { timeout: { hours: 24 }, serdes: defaultSerdes }
    )
    if (!approval.approved) return { status: 'rejected', recommendation: response }
  }

  return { status: event.requireApproval ? 'approved' : 'completed', recommendation: response }
})

Let’s walk through what’s happening in each phase.

Phase 1: Plan

The planning phase is the biggest change from the in-process demo. In the previous post, the coordinator was a single agent that both decided which specialists to call and synthesized their findings. Here, planning and synthesis are separate agents wrapped in separate context.step() calls.

Why split them? Checkpointing. In the in-process demo, if the coordinator crashes after calling three specialists, you lose the routing decision and all three results. With durable functions, the plan is checkpointed as a single unit. If the function replays after the plan step, it returns the cached plan instantly without calling Bedrock again.

The planning agent uses tool() with a Zod schema to produce structured output. The create_analysis_plan tool captures the plan into a closure variable, and the step returns it as its checkpointed result. If the agent doesn’t call the tool, the step throws an error. The retry strategy will attempt it three times, but this is a non-transient failure: if the model didn’t call the tool on the first attempt, retries won’t help. After retries are exhausted, the execution fails.

Phase 2: Parallel specialists

This is where context.invoke() replaces tool() callbacks. Each specialist invocation is a branch inside context.parallel():

const result = await ctx.invoke<{ prompt: string }, { response: string }>(
  spec.name,             // step name for checkpoint history
  specialist.functionName, // Lambda function name from environment
  { prompt: spec.prompt }  // payload
)

context.invoke() calls the specialist Lambda function directly, checkpoints the result, and suspends the coordinator while waiting. You don’t pay for coordinator compute while specialists are executing. On replay, completed invocations return their cached results without re-invoking the specialist.

Each branch has a try/catch for graceful degradation. If the Delivery specialist times out, the coordinator still gets results from Price Research, Financing, Risk Assessment, and Contract Review. The synthesis agent notes the gap and advises the buyer to investigate delivery independently. This is a meaningful improvement over the in-process demo, where a failed specialist tool call could derail the entire coordinator.

Phase 3: Synthesize

The synthesis agent receives all specialist findings and produces a structured recommendation. Like the plan step, the entire synthesis is one checkpointed unit. The synthesis prompt’s Narrowing section prevents it from overriding specialist findings or filling in gaps from failed specialists.

Phase 4: Human approval

When requireApproval is true (the used car scenario sets this), the function suspends at waitForCallback. Compute charges stop. The approver reviews the recommendation and sends a callback via the Lambda API or the demo’s interactive prompt. The function resumes and returns the final status.

Note the serdes: defaultSerdes option on the callback. As covered in the durable functions post’s gotchas, waitForCallback defaults to passthrough serialization (not JSON.parse). Without defaultSerdes, approval.approved would be undefined at runtime even though TypeScript thinks it’s a boolean.

The Specialist Handler

All five specialists share one handler. The PROMPT_NAME environment variable selects the behavior:

const promptName = process.env.PROMPT_NAME!
const systemPrompt = loadPrompt(promptName)
const model = createModel(SPECIALIST_MODEL)

export const handler = async (
  event: SpecialistEvent,
  context: Context
): Promise<{ response: string }> => {
  const logger = makeLogger({ handler: promptName, requestId: context.awsRequestId })
  const tools = loadSpecialistTools(promptName, logger)
  const agent = new Agent({ model, systemPrompt, tools, printer: false })
  const result = await agent.invoke(event.prompt)
  return { response: result.toString() }
}

Specialists are standard Lambda functions, not durable. They don’t need checkpointing because each one completes in a single invocation (a Bedrock call and response processing). The coordinator’s context.invoke() handles the durability: if a specialist invocation times out, the coordinator can retry from the checkpoint without re-running specialists that already succeeded.

loadSpecialistTools returns specialist-specific tools (see src/lib/specialist-tools.ts). Most specialists are pure reasoning (no tools). The Price Research specialist has a save_price_snapshot tool that logs structured price data. In production, that tool could write to DynamoDB or call a pricing API. The coordinator never sees these tools. They’re scoped to each specialist’s domain.

The Prompt Split

The in-process demo had one coordinator prompt with routing in the Steps section. The durable version splits this into two prompts:

coordinator-plan handles routing. Here’s the Steps and Expectation sections:

# Steps
1. Read the purchase request and identify what is being purchased, its likely category
   (vehicle, electronics, real estate, software, etc.), the approximate value range,
   and any special circumstances mentioned or implied (used/secondhand, financing needed,
   physical delivery, contract or subscription, high value).
2. Select specialists using these routing rules:
   - Always include price-research to compare options and assess market value.
   - Include financing if the estimated value exceeds $5,000, or if financing or a loan
     is mentioned or implied.
   - Include delivery if the item is a tangible physical product that must be physically
     received — electronics, appliances, vehicles, furniture, machinery. Vehicles always
     need delivery planning (transport, pickup, or test drive logistics). Do not include
     for purely digital purchases (software, SaaS subscriptions, downloadable content).
   - Include contract-review if the purchase involves a subscription, lease, warranty
     agreement, service contract, purchase agreement, or any multi-year financial
     commitment. Vehicle and real estate purchases always involve contracts.
   - Include risk-assessment if the estimated value exceeds $10,000, the item is used or
     secondhand, or the category carries known risk (vehicles, real estate, machinery).
     Do not include for new electronics, appliances, or standard retail under $10,000.
3. For each selected specialist, write a focused prompt describing what to analyze about
   this specific purchase. Include relevant details from the request (item, price,
   condition, location, urgency). The specialist's own system prompt defines its role —
   do not repeat the role description in your prompt.
4. Call create_analysis_plan exactly once with the selected specialists and their prompts.

# Expectation
A single call to create_analysis_plan containing:
- An array of specialists, each with a name (from the allowed set) and a prompt string.
- Only specialists whose routing criteria are met.
- Prompts that are specific to this purchase, not generic templates.

These are the same routing rules from the original coordinator prompt in the multi-agent post. The difference: instead of calling specialist tools directly (Steps 2-6 in the original said “invoke the research_prices tool,” “invoke the evaluate_financing tool”), the plan agent calls create_analysis_plan once with all selected specialists. The actual dispatch happens via context.invoke() in Phase 2.

coordinator-synthesis handles the final recommendation. It receives specialist findings and produces the buyer-facing output. Its Narrowing prevents it from contradicting specialists or filling in missing analysis.

This split means the coordinator makes two agent invocations (plan + synthesize) instead of one. Each agent invocation may involve multiple Bedrock round-trips internally as the Strands SDK handles reasoning. The trade-off is worth it: the plan is checkpointed before any specialist is called, and the synthesis is checkpointed after all specialists complete. On replay, both return cached results instantly.

Testing with the Local Runner

The durable functions post showed LocalDurableTestRunner for the support triage workflow. The multi-agent demo adds a new pattern: registerFunction for mocking context.invoke() targets.

// Register mock handlers for each specialist Lambda function
runner = new LocalDurableTestRunner({ handlerFunction: handler });
runner
  .registerFunction("PriceResearchFunction", specialistHandler)
  .registerFunction("FinancingFunction", specialistHandler)
  .registerFunction("DeliveryFunction", specialistHandler)
  .registerFunction("RiskAssessmentFunction", specialistHandler)
  .registerFunction("ContractReviewFunction", specialistHandler);

// Run the full workflow and verify the result
const result = runner.run({ payload: laptopPayload });
const output = await result;
expect(output.getStatus()).toBe("SUCCEEDED");
expect(output.getResult().routing.called).toContain("Price Research");

registerFunction maps function names to local handlers. When the coordinator calls context.invoke("PriceResearchFunction", payload), the test runner routes it to the registered mock instead of calling Lambda. This lets you test the full checkpoint/replay lifecycle without deploying or calling Bedrock. The full test suite also tests callback suspension and resumption using runner.getOperation() and sendCallbackSuccess(), the same pattern from the durable functions post.

The test suite covers seven scenarios: standard flow, all-5-specialists flow, approval, rejection, specialist failure with graceful degradation, callback failure, and planning agent failure.

Try the Demo

The repo includes an interactive demo with three purchase scenarios:

Scenario Value Specialists Approval
Laptop $1,500 Price Research + Delivery No
Used car $18,000 All 5 specialists Yes (waitForCallback)
SaaS subscription $200/mo Price Research + Contract Review Yes (waitForCallback)

You’ll need:

  • Node.js 24+ and npm
  • For cloud mode: AWS SAM CLI 1.153.1+ and Bedrock access to Claude Sonnet 4.6 and Claude Haiku 4.5

Local mode (no AWS credentials needed)

git clone https://github.com/gunnargrosch/durable-multi-agent-purchasing.git
cd durable-multi-agent-purchasing
npm install
npm run demo:local -- --ticket=used-car

Local mode uses mocked Bedrock responses. The used car scenario exercises all four durable primitives: step (plan and synthesis), invoke (specialist calls), parallel (concurrent dispatch), and waitForCallback (human approval where you play the purchase approver).

Cloud mode (real Bedrock responses)

sam build
sam deploy --guided
npm run demo:cloud -- --ticket=used-car --region=us-east-1

Cloud mode invokes the deployed coordinator with real Bedrock calls. You’ll see actual AI-generated specialist analyses and a synthesized recommendation. The demo polls execution history and prompts you when the approval callback is created.

The demo uses direct aws lambda invoke with --invocation-type Event for simplicity. In production, the coordinator would typically sit behind an upstream service: an API Gateway endpoint receiving purchase requests, an EventBridge rule triggered by order events, or an SQS queue processing a backlog. The coordinator itself doesn’t care how it’s invoked. It receives the event payload and the durable SDK handles the rest.

Inspecting execution history

After a cloud run, you can inspect the execution in the Lambda console under the Durable executions tab, or via the CLI:

aws lambda list-durable-executions-by-function 
  --function-name durable-multi-agent-purchasing-CoordinatorFunction

aws lambda get-durable-execution 
  --durable-execution-arn "<arn-from-list>"

The execution history shows each step’s status and timing: when the plan completed, how long each specialist took, and whether the callback is pending or resolved.

Design Decisions

context.invoke() over HTTP

The multi-agent post previewed two deployment approaches: Lambda with API Gateway/Function URLs (HTTP), or AgentCore containers. This demo takes a third path: context.invoke() for direct Lambda-to-Lambda invocation.

The result is simpler than either preview option. No API Gateway resources, no function URLs, no SigV4 signing, no HTTP client configuration. The coordinator calls specialists via the Lambda API, and the durable SDK handles checkpointing and retry. Specialists are unreachable from outside the coordinator’s execution role, which is better isolation than an HTTP endpoint with IAM auth.

The trade-off: specialists are only callable from within a durable function. If you later need specialists accessible from other services (a REST API, a Step Functions state machine, another team’s coordinator), you’d need to add API Gateway or function URLs at that point. For this use case, where one coordinator owns all specialist dispatch, direct invocation is the right call.

Splitting plan from synthesis

The in-process coordinator was a single agent invocation: read the request, call specialist tools, synthesize findings. With durable functions, that becomes two separate agents wrapped in separate context.step() calls.

This introduces an extra Bedrock call, but the checkpointing benefits are significant. The plan is preserved before any specialist runs. If the function replays after three of five specialists complete, the plan doesn’t need to be regenerated. The synthesis is preserved after all specialists complete. If the function replays during the approval callback, the recommendation doesn’t need to be regenerated.

The alternative would be wrapping the entire coordinator in a single step. That would mean one Bedrock conversation (cheaper) but no intermediate checkpoints. A failure during synthesis would replay from the beginning, including all specialist invocations. With the split, a synthesis failure only retries the synthesis.

Planning agent with tool capture

The planning agent uses tool() with a Zod schema to produce structured output. This is the same pattern from the in-process demo, but used differently. In the previous post, tools were the dispatch mechanism (calling tools = calling specialists). Here, the tool is purely for structured output capture. The plan step returns the captured plan as its checkpointed result, and context.invoke() handles the actual specialist dispatch.

Why not just have the planning agent return JSON directly? Tool calling with a schema gives you validation at the SDK level. If the agent returns a plan with an invalid specialist name, Zod catches it before the plan is checkpointed. Without the tool, you’d parse and validate the JSON yourself, and an invalid plan could be checkpointed and break on every subsequent replay.

Graceful degradation in parallel

Each specialist branch in context.parallel() has a try/catch that returns an error message instead of throwing:

catch (err) {
  return { name: spec.name, response: `[unavailable: ${err instanceof Error ? err.message : 'unknown error'}]` }
}

This means a failed specialist doesn’t fail the entire parallel block. The synthesis agent receives partial results and notes the gap. Without the catch, a failed specialist would fail its branch. Whether that fails the entire parallel block depends on the completion config, but either way, synthesis would never run with partial results. With the catch, every branch succeeds (some with error messages), the parallel block always completes, and the synthesis agent can work with whatever it has.

Cost

The main cost is Bedrock token usage, not Lambda compute.

Scenario Specialists Agent invocations Estimated cost
Laptop (2 specialists) 2 ~4 (plan + 2 specialists + synthesize) ~$0.02-0.05
Used car (5 specialists) 5 ~7 (plan + 5 specialists + synthesize) ~$0.05-0.15

The coordinator uses Sonnet (~$3/$15 per million input/output tokens). Most specialists use Haiku (~$1/$5 per million input/output tokens). Risk Assessment uses Sonnet. Lambda compute for the active execution periods (plan, specialist dispatch, synthesis, replay) totals ~$0.0001. During the waitForCallback suspension, compute charges are zero.

Things to Watch For

Checkpoint size. Each step result is serialized and stored as a checkpoint. The durable functions post covered the 256KB limit per checkpoint. With 5 specialists returning Bedrock responses, the parallel result could get large if the model is verbose. Monitor response sizes. If you hit the limit, truncate or summarize specialist responses before returning them from the branch, or store full responses in S3 and return a reference.

Debugging failures. If the plan step fails after exhausting retries, or a specialist consistently times out, the execution moves to FAILED status. Use get-durable-execution to see which step failed and the error message. The coordinator uses context.logger (replay-aware), so CloudWatch Logs show each phase’s progress without duplicate lines from replays. Specialist failures are easier to debug since each specialist has its own log group (sam logs --name PriceResearchFunction --tail).

Replay safety. Durable functions re-execute your handler from the top on every resume. Completed context.step() and context.invoke() calls return cached results, but any code outside those primitives runs again on every replay. If you add a side effect (writing to DynamoDB, sending a notification, calling an external API), wrap it in a context.step() so it executes exactly once. The coordinator’s comment on the save-recommendation step shows this pattern. Without the step wrapper, you’d send duplicate notifications every time the function replays.

Cold starts. The used car scenario invokes 5 specialist Lambda functions in parallel. If all 5 are cold, that’s 5 concurrent cold starts: arm64 Node.js 24, the Strands SDK, and the Bedrock client. This can add several seconds to the first specialist round. The in-process demo had no cold start penalty for specialists since everything ran in one process. In practice, the cold start overhead is small relative to the Bedrock inference time that follows. For workflows that already include multi-second model calls and human approval, a few extra seconds on the first invocation is rarely the bottleneck.

Wrapping Up

This post showed how to take a multi-agent system from a single-process demo to a deployed, fault-tolerant system on Lambda with durable functions. The RISEN prompts carry over with minimal changes. The architectural shift is from in-process tool calls to checkpointed Lambda-to-Lambda invocations with independent scaling, failure isolation, and human-in-the-loop approval.

Additional Resources

What multi-agent workflow would you deploy with durable functions? Let me know in the comments!

Santa Augmentcode Intent Ep.5

Finishing Before Christmas — Spec-Driven Development 📜

Accompanying source code repository: Santa Augmentcode Intent

Do you know why Christmas always arrives on time? Not because I am superhuman. Not because the reindeer are faster than physics should allow. Christmas arrives on time because of one inviolable rule in the North Pole: nothing gets built until we agree, in writing, on what done looks like. We call it the Master Gift List. The world calls it Spec-Driven Development. The result is the same: no surprises on Christmas morning.

The Old Way: Code First, Regret Later

There is a seductive pattern in software development that I call Build First, Discover Later. It goes like this:

  1. Someone has a rough idea.
  2. A developer (or, increasingly, an agent) starts building immediately.
  3. Halfway through, the stakeholder sees a demo and says: “That is not what I meant at all.”
  4. Everything is reworked. The deadline slips. Someone’s Christmas is ruined.

I have witnessed this pattern in the Workshop. A zealous Elf, eager to begin, starts carving a rocking horse before Father Christmas has decided whether it should be red or blue, large or small, with or without a mane. Three hours later, the horse is red, large, and maned — and the child’s letter clearly specified blue, small, no mane.

The problem is not the Elf’s carving ability. The problem is that work began before intent was explicit.

What Is Spec-Driven Development?

Spec-Driven Development (SDD) is a workflow practice, not a programming paradigm. It has one central principle:

The spec is the primary artifact. Code is the result of executing it.

In SDD, you do not write code and then document it. You write a spec — a precise, executable description of what you intend to build — and then agents (or humans) implement it. The spec comes first. The spec stays current. The spec is what gets reviewed.

Augment Intent was built around this principle. Their product manifesto states it plainly: at that point, the plan becomes the product.

The Three Pillars of SDD in Intent

Pillar 1: Write the Spec Before Any Code

The Coordinator Agent will not spawn Specialists until a spec exists. This is not bureaucracy — it is discipline. A spec written before implementation captures the human’s intent (what success looks like, what constraints exist, what decisions have been made) rather than narrating code that has already been written.

A good pre-implementation spec answers:

  • What is the goal, measured how?
  • What must not change (frozen modules, existing contracts, budget)?
  • What does “done” look like in a test?
  • Which parts depend on which others?

Pillar 2: The Spec Updates as Work Progresses

A spec that does not evolve becomes fiction. Intent’s living spec updates automatically as agents complete tasks, record decisions, and encounter new constraints. When you return to the spec after a day of agent work, it reflects what was actually built, not what was originally planned.

This closes the gap that traditionally exists between the plan and the product. In most organisations, that gap is enormous and filled with undocumented decisions, tribal knowledge, and polite disagreements about what the requirements “really meant.”

Pillar 3: Review the Spec, Not Just the Code

Code review is expensive. Reviewing a 2,000-line pull request is difficult, slow, and error-prone. Reviewing a spec is cheap. A well-structured spec can be reviewed in minutes, catching fundamental misalignments before a single line of code is written.

SDD moves the review point earlier in the pipeline. You review intent first. Implementation follows. This is why Intent has the Verifier Agent checking output against the spec — because the spec is the ground truth, and the code is measured against it.

The Christmas Deadline as a Forcing Function

Father Christmas has one advantage over most product teams: an unmovable deadline. Christmas is December 25th. Always. Everywhere. It does not slip. It does not move to accommodate a stakeholder who changed their mind in November.

This deadline is a gift (pun intended). It forces the Workshop to be explicit about scope from the very beginning. We cannot add “one more toy” on December 20th without removing something else. Every addition requires a trade-off. The Master Gift List enforces this.

SDD imposes the same discipline on software teams. When the spec defines the scope, additions are visible. You can see exactly what is in and what is out. Scope creep does not happen silently — it requires a spec change, which requires a decision, which requires a conversation. That conversation, had early, is cheap. Had at 11pm on December 24th, it is catastrophic.

SDD vs Traditional Approaches

Aspect Traditional Spec-Driven
Source of truth Code Living Spec
When specs are written After or alongside code Before code
Spec accuracy over time Drifts (document rot) Stays current (auto-updated)
Agent alignment Re-explain each session Automatic via spec
Review point Code review only Spec review + code review
Scope control Implicit, often invisible Explicit, requires spec change
Refactoring cost Break and fix Spec-guided

SIPOC: Spec-Driven Development Workflow

S — Suppliers I — Inputs P — Process O — Outputs C — Customers
Who/What Product owner, developer, Coordinator Agent Business goal, constraints, success criteria Write spec → Review spec → Spawn agents → Agents implement → Spec updates → Verifier checks Working software that matches intent, accurate documentation, decision log Engineering team, product owner, QA, end users
Workshop Management, Head Elf Pepper, Father Christmas Child’s wish list, Workshop constraints, quality standards Write the Master List → Agree on scope → Elves build → List updates → QC checks Gifts that match the wish list, accurate gift manifest, no surprises Children, parents, Father Christmas himself

A Worked Example: The Christmas Eve Delivery App

Let us say Head Elf Pepper wants to build a real-time delivery tracker for the sleigh. The old way:

Pepper: "Build a delivery tracker."
Agent: [builds something]
Pepper: "That is not what I wanted."
[Repeat until December 24th.]

The SDD way:

## Christmas Eve Delivery Tracker — Spec

### Goal
Real-time map showing sleigh position and remaining deliveries,
updating every 30 seconds, accessible on elves' tablets.

### Success Criteria
- [ ] Map renders in < 1 second on standard tablet browser
- [ ] Position updates within 30s of sleigh movement
- [ ] Remaining house count is accurate (matches manifest)
- [ ] Works offline (last known position shown if signal lost)

### Constraints
- Must use existing Reindeer GPS API (v2)
- No server-side changes to Delivery Manifest Service
- Must work in Safari (elves use iPads)

### Out of Scope (v1)
- Historical route replay
- Per-gift tracking
- Weather overlay

### Tasks
- [ ] Implement GPS polling service — Tracker Elf
- [ ] Build map component — Map Elf
- [ ] Integrate manifest API — Data Elf
- [ ] Offline fallback — Resilience Elf
- [ ] Integration tests — Test Elf

This spec takes ten minutes to write and saves days of rework. The Elves know exactly what to build. Pepper reviews the spec and agrees it is correct before a line of code is written. The Verifier knows exactly what to check.

This is SDD. This is how Christmas gets delivered on time.

What Comes Next

In Episode 6, Father Christmas will explain the Context Engine — the magical knowledge base that ensures every Elf in the Workshop understands every toy design, every material property, and every past decision. Without it, SDD is impossible at scale.

The plan is the product. Everything else is wrapping paper.Ho ho ho! 🎅

Part of the Santa Augmentcode Intent series. Published on dev.to under the the-software-s-journey organisation.

Santa Augmentcode Intent Ep.2

The Master Gift List That Writes Itself 🎄

Accompanying source code repository: Santa Augmentcode Intent

Every year, on the first of December, I sit at my great oak desk and open the Master Gift List. In the old days, I wrote it once and hoped for the best. By the fifteenth, it bore little resemblance to reality. An Elf had improvised. A supplier had changed a toy’s colour. Three children had written amended letters. The List lied to me — and I only found out on Christmas Eve.No more.

The Problem With Static Specifications

In software, as in Christmas, requirements arrive in waves. The product owner changes her mind. The designer refines the mockup. The security review adds three new constraints.

Traditional specifications — whether a Confluence page, a Notion doc, or a PDF handed over at the start of a sprint — share one fatal flaw: they are written once and then abandoned. The moment the first line of code is committed, the spec begins to drift. By the time you ship, nobody is quite sure what the original intention was.

Fred Brooks called this “document rot.” Father Christmas calls it “the Lying List,” and it has ruined more Christmas Eves than I care to admit.

The Living Spec: What Intent Does Differently

In Augment Intent, the specification is not a document you write and forget. It is a living spec — a Markdown file that lives inside your workspace and evolves as work progresses.

Here is how it works:

1. You seed the spec. Head Elf Pepper (that is you) types a goal into the Coordinator. The Coordinator drafts an initial spec: requirements, architecture decisions, task breakdown, success criteria.

2. The spec is the source of truth. Every Specialist Agent receives the spec as its briefing. Not a copy of a chat history. Not a vague summary. The actual, versioned spec.

3. As Elves complete tasks, the spec updates. When the Auth Agent finishes the token service, the spec marks that task complete. When the API Agent discovers a complication, the spec records the decision. The spec stays accurate because it is being written by the same agents that are doing the work.

4. When you change your mind, the update propagates. If Pepper changes a constraint mid-flight, the Coordinator updates the spec and re-briefs any Elves whose work is affected. No more Elves building to an outdated design.

A Tale of Two Lists

Let me show you the difference in concrete terms.

The Old Way (Static List):

Ticket XMAS-1234
Title: Improve gift delivery speed
Description: Make it faster. Children are waiting.
Assigned to: @delivery-elf

This ticket is, forgive my language, useless to an agent. What does “faster” mean? Faster than last year? Faster than a competitor? Faster per route, or faster in aggregate? An Elf handed this will make a reasonable guess — and the guess may be entirely wrong.

The New Way (Living Spec):

## Gift Delivery Optimisation — Living Spec

### Goal
Reduce average per-route delivery time by 20% vs Christmas 2024.

### Success Criteria
- [ ] Route calculation completes in < 200ms
- [ ] Zero missed deliveries in smoke test (1,000 houses)
- [ ] Sleigh fuel consumption unchanged or reduced

### Constraints
- Must work with existing reindeer harness API (v3.2)
- No changes to the chimney-descent module (frozen until Jan)

### Tasks
- [x] Profile current route-calculation bottleneck — Dasher Agent
- [ ] Implement A* pathfinding — Prancer Agent (in progress)
- [ ] Integration test against 2024 route dataset — Blitzen Agent
- [ ] Update delivery manifest schema — Rudolph Agent

### Decisions
- 2025-12-03: Chose A* over Dijkstra for weighted graph support
- 2025-12-05: Excluded Antarctic routes from v1 scope

This spec is machine-executable. Every Elf knows what “done” means. Every constraint is explicit. Every decision is recorded. And when Prancer finishes A*, the spec will tick that box and Blitzen will know it is time to start testing.

SIPOC: The Living Spec Process

S — Suppliers I — Inputs P — Process O — Outputs C — Customers
Who/What Developer (Head Elf Pepper), Coordinator Agent, Specialist Agents Initial goal statement, codebase context, model selection Goal → Coordinator drafts spec → Agents execute & update → Spec reflects reality Always-accurate living spec, completed task list, decision log All Specialist Agents, human reviewers, future contributors
Workshop Pepper, Father Christmas, the Elves “Deliver all gifts 20% faster” Santa writes the List → Elves build → List updates as gifts are wrapped A gift list that is always true Every Elf, every Quality Inspector, the Head of Logistics

Why This Matters for Parallel Work

Here is the key insight. When three Elves work simultaneously on different parts of the toy, each one needs to know:

  • What the other two are building.
  • Which interfaces they must respect.
  • What has already been decided.

Without a living spec, each Elf improvises. The train set’s power connector uses a different voltage than the controller the second Elf built. Integration collapses at the last minute.

With a living spec, those interfaces are declared upfront. When the first Elf changes something, the spec records it, and the second Elf’s briefing updates. The spec is the coordination mechanism.

As Augment’s manifesto puts it: once you are running multiple agents in parallel, the spec stops being process and starts being infrastructure.

Practical Notes: Writing a Good Spec Seed

Father Christmas has learned, through decades of painful experience, that the quality of the output depends entirely on the quality of the intent put in. Here are my rules for seeding a Living Spec:

  1. Name the goal, not the solution. Write “reduce onboarding drop-off by 20%”, not “add a progress bar to the onboarding screen”.
  2. State the constraints explicitly. Frozen modules, existing APIs, budget limits — write them down before any Elf starts.
  3. Define done. A measurable success criterion is worth a hundred vague requirements.
  4. Record decisions as they are made. An undocumented decision is an ambiguity waiting to cause a merge conflict.

What Is Coming Next

In Episode 3, Father Christmas will introduce the most important relationship in the Workshop: Santa the Coordinator and the Elves as Specialist Agents. We will examine who does what, how handoffs work, and why the Verifier Elf might be the most important Elf in the building.

The Master Gift List is only as good as the team that follows it. Fortunately, my team is excellent.Ho ho ho! 🎅

Part of the Santa Augmentcode Intent series. Published on dev.to under the the-software-s-journey organisation.

Santa Augmentcode Intent Ep.1

Santa’s Secret Weapon: Welcome to the Workshop! 🎅

Accompanying source code repository: Santa Augmentcode Intent

Ho ho ho! Come in, come in — the fire is warm and the cocoa is hot. Pull up a stool and let Father Christmas tell you a story. Not about reindeer, not about presents — but about the most magical piece of software to land in the Workshop since the invention of the Nice List.

The Problem With Being Father Christmas

Every year it is the same. December arrives like an avalanche, and suddenly Father Christmas has more tasks than minutes.

The chimneys of the world do not care that Jingle-Bell the Elf is busy repainting the rocking horses while Twinkle the Elf is still debugging the train set firmware. The world expects one coordinated, perfectly wrapped result under every tree by Christmas morning.

For centuries, I managed this with clipboards, coloured yarn on a corkboard, and a great deal of shouting across the workshop floor. Then, this year, Head Elf Pepper handed me a laptop and said: “Santa, you need to see Augment Intent.”

Reader, I wept a single dignified tear into my beard.

What Is Augment Intent?

Augment Intent is a macOS developer workspace designed for the age of AI agents. It is not merely a chat window where you ask a single Elf to do one thing. It is a coordinated workshop where:

  • A Coordinator (that would be me, Father Christmas) breaks down a complex goal into a living Spec.
  • Specialist Agents (my Elves) run in parallel, each tackling their portion of the work.
  • A Verifier Agent checks that what was built actually matches the Spec.
  • The entire workspace — code, browser, terminal, git — lives in one window, and the state persists when you close the lid.

In short: it is the North Pole Workshop, but running on silicon instead of snowflakes.

The North Pole Analogy, Explained

Before we dive into the technical magic in later episodes, let me introduce the cast of characters you will meet throughout this series.

The Workshop Augment Intent
Father Christmas Coordinator Agent
Head Elf Pepper You, the Developer
The Elves Specialist Agents (Implement, Verify, Debug, Review…)
The Master Gift List The Living Spec
Each Elf’s Workbench Isolated Workspace / Git Worktree
The Workshop Floor Intent’s unified window
The Context Engine The Workshop’s shared knowledge of every toy design, material, and elf skill

The magic of Christmas has always been parallelism with coordination. Teddy-bear Elves do not wait for train-set Elves to finish. They work simultaneously, guided by the same Master Gift List. That is precisely what Intent enables for software development.

What This Series Covers

Over the coming episodes, Father Christmas will walk you through Intent one concept at a time, always in the style of a cosy fireside chat. Here is the full gift list:

Episode Topic
1 Welcome to the Workshop (you are here!)
2 The Master Gift List — Living Specs
3 Santa & the Elves — Coordinator vs Specialist Agents
4 Every Elf Has a Workbench — Isolated Workspaces
5 Finishing in Time for Christmas — Spec-Driven Development
6 The Context Engine — The Workshop Knows Every Toy
7 Parallel Elves, No Chaos — Multi-Agent Orchestration
8 The Delivery — From Spec to Merged Pull Request

SIPOC: The Workshop at a Glance

Before any Elf touches a piece of wood, Father Christmas draws the SIPOC on the big blackboard. Here is Intent’s SIPOC.

S — Suppliers I — Inputs P — Process O — Outputs C — Customers
Who/What provides Developer intent, codebase, models (Claude Opus, Sonnet, GPT…) Task description, repository files, rules & guidelines Spec creation → Coordinator planning → Parallel agent execution → Verification Working code, updated spec, pull request Engineering team, end users, CI/CD pipeline
Workshop equivalent Head Elf Pepper, the toy blueprints, the Elf skill registry The gift order, the raw materials, the Elf roster Santa writes the Master List → assigns Elves → Elves build in parallel → quality check Wrapped, labelled gifts → sleigh Children of the world

Why Should You Care?

You may be thinking: “Santa, I already have VS Code and a single agent. Why do I need a whole workshop?”

Fair question. The answer is the same reason I do not gift-wrap eight billion presents with one pair of hands. Beyond a certain complexity, serial work with a single agent creates a bottleneck. The agent re-explains context, loses state between sessions, and cannot parallelise.

As Augment’s own manifesto puts it: parallel execution is no longer the hard part — coordination is. Intent solves the coordination problem.

Getting Started

Intent is currently in public beta for macOS. You can download it here. It works with your existing Augment credits, and you can even bring Claude Code, Codex, or OpenCode if you already have a subscription for those.

In the next episode, Father Christmas will introduce The Master Gift List — the Living Spec that keeps every Elf pointed at the same star.

Until then: stay warm, write good specs, and remember — Christmas is only ever early for developers who plan ahead.Ho ho ho! 🎅

Part of the Santa Augmentcode Intent series. Published on dev.to under the the-software-s-journey organisation.

Podcast: [Video Podcast] Agentic Systems Without Chaos: Early Operating Models for Autonomous Agents

In this episode, Shweta Vohra and Joseph Stein explore what changes when software systems start planning, acting, and making decisions on their own. The conversation distinguishes truly agentic use cases from traditional automation and looks at how architects and engineers should think about boundaries, orchestration, and system design in this new environment.

By Joseph Stein

AWS Load Balancer Controller Reaches GA with Kubernetes Gateway API Support

AWS shipped GA support for Kubernetes Gateway API in its Load Balancer Controller, dumping annotation-based configuration for type-safe CRDs with proper validation. The release handles both L4 (TCP/UDP via NLB) and L7 (HTTP/gRPC via ALB) routing through the Gateway API spec. Teams get cross-namespace routing, automatic certificate discovery, and role separation without cluster-admin permissions.

By Steef-Jan Wiggers

Presentation: Panel: Security Against Modern Threats

The panelists discuss the dramatic escalation of software supply chain threats, from typosquatting to AI-generated vulnerabilities. They explain how to move beyond basic scanning by adopting a zero trust mindset toward CI/CD pipelines and external dependencies.

By Sonya Moisset, Andra Lezza, Stefania Chaplin, Celine Pypaert, Emma Yuan Fang