[{"data":1,"prerenderedAt":327},["Reactive",2],{"work-index":3},[4,150,202,251],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":8,"kicker":10,"summary":11,"cover":12,"coverSmall":13,"coverAlt":14,"role":15,"where":16,"stack":17,"usedBy":22,"stats":23,"next":30,"order":31,"body":32,"_type":144,"_id":145,"_source":146,"_file":147,"_stem":148,"_extension":149},"/work/software-toolkit","work",false,"","Software Toolkit","Internal product · Storm Reply","A Terraform scaffold that provisions serverless infrastructure, mocks APIs locally with AWS SAM and gives every developer their own sandbox per branch.","/3d/cover-software-toolkit.webp","/3d/cover-software-toolkit-640.webp","Clay model of a stack of slabs with a teal branch leading to a smaller sandbox slab","Built it, lead the guild that owns it","Storm Reply UK",[18,19,20,21],"Terraform","AWS SAM","Python","GitHub Actions","20+ engineers",[24,27],{"value":25,"label":26},"20+","engineers using it",{"value":28,"label":29},"2 weeks → hours","new project setup","agentic-delivery-platform",1,{"type":33,"children":34,"toc":141},"root",[35,63,127],{"type":36,"tag":37,"props":38,"children":40},"element","case-section",{"label":39},"The problem",[41,48,53,58],{"type":36,"tag":42,"props":43,"children":44},"p",{},[45],{"type":46,"value":47},"text","Setting up a new project used to take about two weeks before anyone wrote a feature: infrastructure, pipelines, environments and a way to test the API locally.",{"type":36,"tag":42,"props":49,"children":50},{},[51],{"type":46,"value":52},"Every project started it again from scratch. Someone copied the Terraform from the last engagement, pulled out the parts that belonged to that client, and spent the first sprint getting a pipeline to deploy anything at all. By the end of it no two projects looked alike, so nothing learned on one of them moved to the next.",{"type":36,"tag":42,"props":54,"children":55},{},[56],{"type":46,"value":57},"Testing infrastructure was the worse half. A Lambda behind API Gateway is awkward to exercise on a laptop, so the loop was to push a change, wait for the pipeline, and read CloudWatch. That is minutes of waiting to find a typo. And because everyone shared the same deployed environment, two people changing infrastructure at the same time meant one apply landing on top of the other, or a state lock held by whoever started first. The workaround was to take turns, which is a queue rather than a fix.",{"type":36,"tag":42,"props":59,"children":60},{},[61],{"type":46,"value":62},"The toolkit makes the fixes the default. One scaffold, SAM for the local loop, and a stack per branch so nobody waits on anyone.",{"type":36,"tag":37,"props":64,"children":67},{"label":65,"gap":66},"How it works","steps",[68,79,89,117],{"type":36,"tag":69,"props":70,"children":73},"case-step",{"number":71,"title":72},"1","Scaffold the serverless basics",[74],{"type":36,"tag":42,"props":75,"children":76},{},[77],{"type":46,"value":78},"Twelve Terraform modules cover the building blocks of a serverless app, and a wizard scaffolds each new API endpoint so every project starts from the same shape.",{"type":36,"tag":69,"props":80,"children":83},{"number":81,"title":82},"2","Run the API locally",[84],{"type":36,"tag":42,"props":85,"children":86},{},[87],{"type":46,"value":88},"AWS SAM runs the API on your machine against the same Terraform code that deploys it.",{"type":36,"tag":69,"props":90,"children":94},{"number":91,"title":92,"accent":93},"3","A sandbox for every branch","true",[95,109],{"type":36,"tag":42,"props":96,"children":97},{},[98,100,107],{"type":46,"value":99},"The branch name is hashed into its own Terraform state, so anyone can run ",{"type":36,"tag":101,"props":102,"children":104},"code",{"className":103},[],[105],{"type":46,"value":106},"terraform apply",{"type":46,"value":108}," from their laptop without touching anyone else's stack.",{"type":36,"tag":110,"props":111,"children":112},"template",{"v-slot:extra":8},[113],{"type":36,"tag":114,"props":115,"children":116},"branch-diagram",{},[],{"type":36,"tag":69,"props":118,"children":121},{"number":119,"title":120},"4","Keep it tidy",[122],{"type":36,"tag":42,"props":123,"children":124},{},[125],{"type":46,"value":126},"aws-nuke clears out abandoned sandboxes, and Checkov, tflint and pre-commit hooks run on every change.",{"type":36,"tag":37,"props":128,"children":130},{"label":129},"Results",[131,136],{"type":36,"tag":132,"props":133,"children":135},"case-stats",{":items":134},"[{\"value\":\"2 weeks → hours\",\"label\":\"to set up a new project\"},{\"value\":\"20+\",\"label\":\"engineers use it\"},{\"value\":\"Most used\",\"label\":\"internal tool at Storm Reply UK\"}]",[],{"type":36,"tag":42,"props":137,"children":138},{},[139],{"type":46,"value":140},"It's also what the company's internal Promotion App, for goals and feedback, was built on.",{"title":8,"searchDepth":142,"depth":142,"links":143},2,[],"markdown","content:work:software-toolkit.md","content","work/software-toolkit.md","work/software-toolkit","md",{"_path":151,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":152,"description":8,"kicker":153,"summary":154,"cover":155,"coverSmall":156,"coverAlt":157,"role":158,"where":159,"stack":160,"stats":164,"next":171,"order":142,"body":172,"_type":144,"_id":199,"_source":146,"_file":200,"_stem":201,"_extension":149},"/work/agentic-delivery-platform","Agentic delivery platform","Lead engineer · global travel company","Moving a Jira ticket to Agent Ready hands it to agents on Amazon Bedrock AgentCore, which take it through requirements, architecture, implementation and code review.","/3d/cover-agentic-platform.webp","/3d/cover-agentic-platform-640.webp","Clay model of a conveyor with four upright cards travelling along it, one of the tiles on the rail picked out in teal","Lead engineer","Global travel company",[161,162,163,20],"Amazon Bedrock AgentCore","Jira","GitLab",[165,168],{"value":166,"label":167},"3 agents","Claude Code, Codex, Kiro, interchangeable",{"value":169,"label":170},"Per agent","token and cost tracking","alarm-framework",{"type":33,"children":173,"toc":197},[174],{"type":36,"tag":37,"props":175,"children":177},{"label":176},"Overview",[178,182,187,192],{"type":36,"tag":42,"props":179,"children":180},{},[181],{"type":46,"value":154},{"type":36,"tag":42,"props":183,"children":184},{},[185],{"type":46,"value":186},"The agents run on Amazon Bedrock AgentCore, with an orchestration layer in Python sitting between the platform and the tools. That layer is the part I would keep. Claude Code, Codex and Kiro are interchangeable behind it, so which agent does a stage is a configuration choice rather than a rewrite, and every call is logged and traced with its tokens and its cost attributed to the agent that made it.",{"type":36,"tag":42,"props":188,"children":189},{},[190],{"type":46,"value":191},"The handover is the ticket. Jira holds the state, and each stage writes its output back onto the ticket for the next one to read: requirements, then architecture, then implementation, then code review. Moving a ticket to Agent Ready starts the first stage. After that, GitLab webhooks trigger each round of development and review, so a push or a comment on the merge request is what wakes the next pass rather than a person chasing it. Discovery runs locally, with Kiro agents that apply the client's own product guidelines to the requirements before the work reaches the board.",{"type":36,"tag":42,"props":193,"children":194},{},[195],{"type":46,"value":196},"What changed for the team is where their work starts. A ticket that used to be picked up empty now arrives with requirements and a design attached, and comes back as a merge request to read rather than a branch to write. Reviewing is still a person's job, and it is more of the job than it used to be.",{"title":8,"searchDepth":142,"depth":142,"links":198},[],"content:work:agentic-delivery-platform.md","work/agentic-delivery-platform.md","work/agentic-delivery-platform",{"_path":203,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":204,"description":8,"kicker":205,"summary":206,"cover":207,"coverSmall":208,"coverAlt":209,"where":210,"stack":211,"stats":213,"next":220,"order":221,"body":222,"_type":144,"_id":248,"_source":146,"_file":249,"_stem":250,"_extension":149},"/work/alarm-framework","Alarms for a whole AWS organisation","Monitoring · UK national lottery operator","A Python data source finds resources across every account at plan time, and Terraform generates their CloudWatch alarms from per-service specs. A daily GitHub Actions run keeps it current.","/3d/cover-alarm-framework.webp","/3d/cover-alarm-framework-640.webp","Clay model of a grid of small blocks with pin lights on top, two of them lit teal and one dark","UK national lottery operator",[18,20,212,21],"CloudWatch",[214,217],{"value":215,"label":216},"109","alarm modules",{"value":218,"label":219},"Every account","compute, databases, networking","terraform-guard-rails",3,{"type":33,"children":223,"toc":246},[224],{"type":36,"tag":37,"props":225,"children":226},{"label":176},[227,231,236,241],{"type":36,"tag":42,"props":228,"children":229},{},[230],{"type":46,"value":206},{"type":36,"tag":42,"props":232,"children":233},{},[234],{"type":46,"value":235},"The specs are per service, not per resource. One spec says what is worth alarming on for a service, it is written once, and everything of that kind in the organisation inherits it. There are 109 alarm modules behind them, covering compute, databases, networking and the rest of what the estate runs. Nobody writes an alarm by hand.",{"type":36,"tag":42,"props":237,"children":238},{},[239],{"type":46,"value":240},"The resources are found rather than listed. A Python data source runs at plan time, walks every account with boto3 and returns what is actually there. Terraform then pairs the discovered resources with their specs and generates the alarms. Because the discovery happens on every plan, the plan diff is the reconciliation: anything created since the last run turns up with its alarms attached, anything deleted takes its alarms with it, and no alarm is left pointing at a resource that has gone.",{"type":36,"tag":42,"props":242,"children":243},{},[244],{"type":46,"value":245},"A GitHub Actions workflow runs that daily, so a team that launched a database in the morning has alarms on it without filing a request. What the alarms produce goes to the monitoring and alerting team that owns the platform's alerting, next to the Grafana dashboards and Sensu checks covering the same estate.",{"title":8,"searchDepth":142,"depth":142,"links":247},[],"content:work:alarm-framework.md","work/alarm-framework.md","work/alarm-framework",{"_path":252,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":253,"description":8,"kicker":254,"summary":255,"cover":256,"coverSmall":257,"coverAlt":258,"role":259,"where":16,"stack":260,"stats":261,"next":268,"order":269,"body":270,"_type":144,"_id":324,"_source":146,"_file":325,"_stem":326,"_extension":149},"/work/terraform-guard-rails","Terraform with guard rails","Company template · Storm Reply","The starting point for new AWS projects: agent skills for Terraform work, a plan review that flags IAM widening, replacements and cost changes, and tested hooks that block destructive changes.","/3d/cover-terraform-template.webp","/3d/cover-terraform-template-640.webp","Clay model of a dark disc on a plinth, fenced off behind a teal gate","Wrote it",[18,21],[262,265],{"value":263,"label":264},"22","Claude Code and Codex skills",{"value":266,"label":267},"Every PR","plans all environments, gated apply","software-toolkit",4,{"type":33,"children":271,"toc":322},[272],{"type":36,"tag":37,"props":273,"children":274},{"label":176},[275,279,292,297,317],{"type":36,"tag":42,"props":276,"children":277},{},[278],{"type":46,"value":255},{"type":36,"tag":42,"props":280,"children":281},{},[282,284,290],{"type":46,"value":283},"The plan review is a script and a skill working together. ",{"type":36,"tag":101,"props":285,"children":287},{"className":286},[],[288],{"type":46,"value":289},"check_plan.py",{"type":46,"value":291}," reads the JSON plan and classifies its impact. Red for a destroy or a replace, for IAM widening, meaning a star action on a star resource, a star principal or a condition removed, and for ingress opened to the world. Amber for updates, security-relevant types, cost-bearing creates and anything targeting prod. A hook then fires on the fresh plan and tells the agent to run the review-plan skill, which works a fixed checklist of intent match, replacements, IAM and network widening and cost delta, and returns one of three verdicts: pass, pass with concerns, or do not apply.",{"type":36,"tag":42,"props":293,"children":294},{},[295],{"type":46,"value":296},"What that catches is not cleverness. It is the things a human reviewer stops seeing. A replace buried two hundred lines into a diff nobody scrolls to the end of. A wildcard that widens a policy by one character. A condition block removed rather than added. Deletes are handled separately and bluntly: any delete or replace fails the plan outright unless a flag is set, and the rule is that the flag is never set without showing a person the literal list of what goes.",{"type":36,"tag":42,"props":298,"children":299},{},[300,302,308,310,315],{"type":46,"value":301},"Every classification rule has a fixture plan next to it, so changing a rule without changing a test fails ",{"type":36,"tag":101,"props":303,"children":305},{"className":304},[],[306],{"type":46,"value":307},"make check",{"type":46,"value":309},". The hooks work the same way. There are six, each with a test beside it, and ",{"type":36,"tag":101,"props":311,"children":313},{"className":312},[],[314],{"type":46,"value":307},{"type":46,"value":316}," runs all of them. The two that earn their keep refuse to let the agent edit the files that constrain it, the bootstrap stack, the state, the hooks directory and the harness settings, and block a recursive force delete aimed at the repo root.",{"type":36,"tag":42,"props":318,"children":319},{},[320],{"type":46,"value":321},"The template only moves in one direction. A bug found in a hook, a script or a workflow on a live project is filed on the template's own board and fixed there, never patched in place, so the fix reaches every project started from it afterwards.",{"title":8,"searchDepth":142,"depth":142,"links":323},[],"content:work:terraform-guard-rails.md","work/terraform-guard-rails.md","work/terraform-guard-rails",1789331136437]