<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Github-Actions on Worlds of the Next Realm - Dev Blog</title><link>https://ipjohnson-org.github.io/WorldsOfTheNextRealm.Blog/tags/github-actions/</link><description>Recent content in Github-Actions on Worlds of the Next Realm - Dev Blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Tue, 17 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://ipjohnson-org.github.io/WorldsOfTheNextRealm.Blog/tags/github-actions/index.xml" rel="self" type="application/rss+xml"/><item><title>The Development Journey, Part 1: Nine Days from Zero</title><link>https://ipjohnson-org.github.io/WorldsOfTheNextRealm.Blog/p/the-development-journey-part-1-nine-days-from-zero/</link><pubDate>Tue, 17 Feb 2026 00:00:00 +0000</pubDate><guid>https://ipjohnson-org.github.io/WorldsOfTheNextRealm.Blog/p/the-development-journey-part-1-nine-days-from-zero/</guid><description>&lt;p&gt;This is the first of a three-part series covering the development journey of Worlds of the Next Realm so far. In this post, we cover the foundation: standing up infrastructure, building the first services, and getting something on screen.&lt;/p&gt;
&lt;p&gt;The timeline is aggressive. The first commit landed on February 8th, 2026. Nine days later, we had 11 repositories, a live beta environment, a working authentication system, a Flutter web client with isometric tile maps, and a backend serving real game data from DynamoDB.&lt;/p&gt;
&lt;h2 id="day-1-2-the-foundation-sprint-feb-8-10"&gt;&lt;a href="#day-1-2-the-foundation-sprint-feb-8-10" class="header-anchor"&gt;&lt;/a&gt;Day 1-2: The Foundation Sprint (Feb 8-10)
&lt;/h2&gt;&lt;p&gt;Everything started with the Flutter client and the AWS infrastructure.&lt;/p&gt;
&lt;p&gt;The FrontEndClient repo received 12 PRs in the first two days. We scaffolded the entire application structure — domain models, theming, navigation, mock backend layer, and all the core UI screens: troops, leaders, research, guild, inventory, expeditions, barter, events, settings, chat, shop, and AI companion. Most of these screens were populated with mock data, but the architecture was real — Riverpod state management, GoRouter navigation, Dio HTTP client with interceptors.&lt;/p&gt;
&lt;p&gt;The most significant piece was the isometric 2.5D tile map renderer built on the Flame game engine. This would become the city view and world map — the visual heart of the game.&lt;/p&gt;
&lt;p&gt;Simultaneously, the Infra repo went up with CDK stacks for the VPC, DynamoDB tables, Application Load Balancer, and CloudFront distribution. We deployed the BackendApi on Lambda behind API Gateway, and the NotificationService and WorldSimulation on Fargate.&lt;/p&gt;
&lt;p&gt;All three backend services had working CDK deployments by the end of day 2. The CI/CD pipelines were live — every push to main deployed to beta automatically.&lt;/p&gt;
&lt;h2 id="day-3-4-the-data-layer-feb-11-12"&gt;&lt;a href="#day-3-4-the-data-layer-feb-11-12" class="header-anchor"&gt;&lt;/a&gt;Day 3-4: The Data Layer (Feb 11-12)
&lt;/h2&gt;&lt;p&gt;This is where BackendCommon became the backbone of the project.&lt;/p&gt;
&lt;p&gt;We built a generalized DynamoDB data store abstraction — a single-table design where every game entity (players, cities, buildings, resources, troops, worlds) lives in one table with partition key patterns and GSIs for alternate access patterns. The data store handles serialization, optimistic concurrency via version IDs, and batch operations.&lt;/p&gt;
&lt;p&gt;The DynamoDB schema went through several iterations during these two days:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Started with a partition key + sort key design&lt;/li&gt;
&lt;li&gt;Removed the sort key&lt;/li&gt;
&lt;li&gt;Added GSI1&lt;/li&gt;
&lt;li&gt;Added GSI2&lt;/li&gt;
&lt;li&gt;Restored the sort key and added GSI1&lt;/li&gt;
&lt;li&gt;Recreated the table as PK-only with GSI1&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;s 6 PRs in the Infra repo just iterating on the table schema. Each change required coordinating across BackendCommon (data models), Infra (CDK table definition), and the services that read the data. This is one of the costs of a multi-repo architecture — schema changes ripple across repositories.&lt;/p&gt;
&lt;p&gt;The OperationalTools CLI also came to life during this phase: &lt;code&gt;create-user&lt;/code&gt;, &lt;code&gt;load-game-data&lt;/code&gt;, &lt;code&gt;generate-map&lt;/code&gt;, and &lt;code&gt;bootstrap-world&lt;/code&gt; commands. These tools are essential for populating the game world with data — definition files for buildings, troops, resources, and research loaded from JSON into DynamoDB.&lt;/p&gt;
&lt;h2 id="day-5-authentication-and-api-wiring-feb-13"&gt;&lt;a href="#day-5-authentication-and-api-wiring-feb-13" class="header-anchor"&gt;&lt;/a&gt;Day 5: Authentication and API Wiring (Feb 13)
&lt;/h2&gt;&lt;p&gt;February 13th was one of the busiest days of the project. Across all repos, we merged PRs covering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Authentication service&lt;/strong&gt;: Full username/password auth with RS256 JWT tokens, Argon2id password hashing, family-based refresh token rotation, and JWKS endpoint for public key distribution. The master encryption key went through its own evolution — first as a CDK context value, then moved to AWS Secrets Manager for proper secret management.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;JWT middleware in BackendApi&lt;/strong&gt;: All API endpoints now verified authentication tokens. We added request/response models for all 22 planned endpoints and created stub handlers for each one.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Frontend auth integration&lt;/strong&gt;: The Flutter client was hooked up to the real authentication service. The mock login button was removed. Real JWT tokens flowed from login through to API calls.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Structured logging&lt;/strong&gt;: Every service got consistent JSON logging — critical for debugging in a distributed system where you need to correlate requests across Lambda, Fargate, and CloudWatch.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Security hardening&lt;/strong&gt;: CloudFront origin verify headers and WAF rules on the ALB to prevent direct access bypassing the CDN.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This was also the day of the &amp;ldquo;README wave&amp;rdquo; — every repo got a README, documentation links, and design doc references. Housekeeping, but important for a multi-repo project where anyone (human or AI) needs to find their way around.&lt;/p&gt;
&lt;h2 id="the-first-render"&gt;&lt;a href="#the-first-render" class="header-anchor"&gt;&lt;/a&gt;The First Render
&lt;/h2&gt;&lt;p&gt;Getting the isometric city to render with real data in the browser was the first real milestone. The early versions had&amp;hellip; issues.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://ipjohnson-org.github.io/WorldsOfTheNextRealm.Blog/p/the-development-journey-part-1-nine-days-from-zero/missing-icons.png"
	width="3016"
	height="1556"
	loading="lazy"
	
		alt="Early city view with missing navigation icons"
	
 
 title="The first render of the city view. Buildings are placed, the isometric grid works, but the bottom navigation bar icons are all missing — a Flutter web asset deployment issue."
 data-title-escaped="The first render of the city view. Buildings are placed, the isometric grid works, but the bottom navigation bar icons are all missing — a Flutter web asset deployment issue."
 
	
		class="gallery-image" 
		data-flex-grow="193"
		data-flex-basis="465px"
	
&gt;&lt;/p&gt;
&lt;p&gt;The bottom navigation bar icons were missing because of how Flutter web packages and deploys JSON-declared assets. It took a dedicated fix to properly deploy subdirectory JSON assets to S3.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://ipjohnson-org.github.io/WorldsOfTheNextRealm.Blog/p/the-development-journey-part-1-nine-days-from-zero/clouds.png"
	width="3020"
	height="1566"
	loading="lazy"
	
		alt="Cloud rendering dominating the viewport"
	
 
 title="An early attempt at the city exterior. The cloud/fog-of-war effect was supposed to mark unexplored territory, but it completely dominates the view, obscuring everything underneath."
 data-title-escaped="An early attempt at the city exterior. The cloud/fog-of-war effect was supposed to mark unexplored territory, but it completely dominates the view, obscuring everything underneath."
 
	
		class="gallery-image" 
		data-flex-grow="192"
		data-flex-basis="462px"
	
&gt;&lt;/p&gt;
&lt;p&gt;The cloud overlay — intended to obscure the city exterior — was rendering far too aggressively. The terrain and buildings underneath were completely invisible. This would take several more days and multiple PRs to get right.&lt;/p&gt;
&lt;h2 id="what-we-learned"&gt;&lt;a href="#what-we-learned" class="header-anchor"&gt;&lt;/a&gt;What We Learned
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Multi-repo coordination is expensive.&lt;/strong&gt; A schema change in DynamoDB touches Infra (CDK), BackendCommon (models), BackendApi (endpoints), OperationalTools (data loading), and sometimes the FrontEndClient (API contracts). The NuGet package pipeline adds latency — you merge a BackendCommon PR, wait for CI to publish the package, then update dependent repos. We ended up with explicit rules: create the BackendCommon PR first, wait for CI, then create dependent PRs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mock-first works.&lt;/strong&gt; Building the entire Flutter client with mock backends first meant we could iterate on UI and navigation without waiting for the real APIs. When the APIs were ready, we swapped in real repositories one at a time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CDK schema iteration is painful.&lt;/strong&gt; Six PRs to get the DynamoDB table right. Each one required a CDK deploy, which means CloudFormation stack updates, which means waiting for DynamoDB table operations to complete. Some of these were destructive — dropping and recreating the table. In a production environment, this would require careful migration planning.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="stats-days-1-5-feb-8-13"&gt;&lt;a href="#stats-days-1-5-feb-8-13" class="header-anchor"&gt;&lt;/a&gt;Stats: Days 1-5 (Feb 8-13)
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Metric&lt;/th&gt;
 &lt;th&gt;Value&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Repositories created&lt;/td&gt;
 &lt;td&gt;11&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;PRs merged&lt;/td&gt;
 &lt;td&gt;143&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Commits&lt;/td&gt;
 &lt;td&gt;~310&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Services deployed&lt;/td&gt;
 &lt;td&gt;5 (BackendApi, AuthService, NotificationService, WorldSimulation, FrontEndClient)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;CDK stacks&lt;/td&gt;
 &lt;td&gt;5 (VPC, DataStore, Web/ALB, CloudFront, PipelineOIDC)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;DynamoDB schema iterations&lt;/td&gt;
 &lt;td&gt;6&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Backend endpoints stubbed&lt;/td&gt;
 &lt;td&gt;22&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Flutter screens built&lt;/td&gt;
 &lt;td&gt;17&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;NuGet packages published&lt;/td&gt;
 &lt;td&gt;3 (BackendCommon, BackendCommon.Cdk, BackendCommon.Testing)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;CI/CD pipelines&lt;/td&gt;
 &lt;td&gt;8 (one per deployable repo)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="code-written-as-of-feb-13"&gt;&lt;a href="#code-written-as-of-feb-13" class="header-anchor"&gt;&lt;/a&gt;Code Written (as of Feb 13)
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Language&lt;/th&gt;
 &lt;th&gt;Lines&lt;/th&gt;
 &lt;th&gt;Purpose&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Dart&lt;/td&gt;
 &lt;td&gt;~14,000&lt;/td&gt;
 &lt;td&gt;Flutter client&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;C#&lt;/td&gt;
 &lt;td&gt;~6,500&lt;/td&gt;
 &lt;td&gt;Backend services + shared libraries&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;TypeScript&lt;/td&gt;
 &lt;td&gt;~520&lt;/td&gt;
 &lt;td&gt;CDK infrastructure&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Markdown&lt;/td&gt;
 &lt;td&gt;~30,000&lt;/td&gt;
 &lt;td&gt;Design docs, game data, READMEs&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;JSON&lt;/td&gt;
 &lt;td&gt;~18,000&lt;/td&gt;
 &lt;td&gt;Game definitions, config&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>The Development Journey, Part 3: What We Learned</title><link>https://ipjohnson-org.github.io/WorldsOfTheNextRealm.Blog/p/the-development-journey-part-3-what-we-learned/</link><pubDate>Tue, 17 Feb 2026 00:00:00 +0000</pubDate><guid>https://ipjohnson-org.github.io/WorldsOfTheNextRealm.Blog/p/the-development-journey-part-3-what-we-learned/</guid><description>&lt;p&gt;This is the final part of our three-part development journey series. &lt;a class="link" href="https://ipjohnson-org.github.io/WorldsOfTheNextRealm.Blog/p/dev-journey-part-1/" &gt;Part 1&lt;/a&gt; covered the foundation. &lt;a class="link" href="https://ipjohnson-org.github.io/WorldsOfTheNextRealm.Blog/p/dev-journey-part-2/" &gt;Part 2&lt;/a&gt; covered building the game features. This post covers what we learned — about the process, about AI-assisted development, and about what the numbers actually tell us.&lt;/p&gt;
&lt;h2 id="the-process-evolved"&gt;&lt;a href="#the-process-evolved" class="header-anchor"&gt;&lt;/a&gt;The Process Evolved
&lt;/h2&gt;&lt;p&gt;When the project started on February 8th, the process was simple: write code, push to main. By February 17th, we had a structured system of rules, templates, and guardrails. Every rule exists because something went wrong.&lt;/p&gt;
&lt;h3 id="workspace-boundaries"&gt;&lt;a href="#workspace-boundaries" class="header-anchor"&gt;&lt;/a&gt;Workspace Boundaries
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;The rule&lt;/strong&gt;: Never look in or modify files above the repository root. Each workspace is independent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it exists&lt;/strong&gt;: In a multi-repo project with an AI coding assistant, context bleed is a real risk. Claude might read a file in BackendApi and then make assumptions about BackendCommon based on what it saw — assumptions that could be wrong if the repos are at different points in their development. Keeping each workspace isolated forces explicit cross-repo coordination through published packages and documented interfaces.&lt;/p&gt;
&lt;h3 id="nuget-versioning"&gt;&lt;a href="#nuget-versioning" class="header-anchor"&gt;&lt;/a&gt;NuGet Versioning
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;The rule&lt;/strong&gt;: Never manually change &lt;code&gt;&amp;lt;Version&amp;gt;&lt;/code&gt; in &lt;code&gt;.csproj&lt;/code&gt; files. Never run &lt;code&gt;dotnet nuget push&lt;/code&gt;. CI handles all versioning and publishing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it exists&lt;/strong&gt;: The BackendCommon package is consumed by 4 other repos. The CI pipeline uses &lt;code&gt;github.run_number&lt;/code&gt; as the patch version, giving every build a unique version. Early in the project, manual version bumps caused conflicts where two builds produced the same version number. The fix was simple — take humans and AI out of the versioning loop entirely.&lt;/p&gt;
&lt;p&gt;The cross-repo workflow is now explicit: create the BackendCommon PR first, wait for CI to publish the new package version, then create PRs in the dependent repos. The dependent repos use &lt;code&gt;0.1.*&lt;/code&gt; wildcard references so they pick up new patches automatically.&lt;/p&gt;
&lt;h3 id="pr-workflow"&gt;&lt;a href="#pr-workflow" class="header-anchor"&gt;&lt;/a&gt;PR Workflow
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;The rule&lt;/strong&gt;: Every PR requires three things: the PR itself, a review comment covering reasoning and concerns, and a session stats comment with token usage and interaction quality metrics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it exists&lt;/strong&gt;: The stats comment requirement was added after several PRs were created without tracking information. Since the human-AI collaboration is itself something we&amp;rsquo;re studying, losing that data means losing insight into what&amp;rsquo;s working. The &amp;ldquo;never defer&amp;rdquo; requirement prevents the stats from being forgotten — they must be posted in the same step as creating the PR.&lt;/p&gt;
&lt;h3 id="the-open-source-mistake"&gt;&lt;a href="#the-open-source-mistake" class="header-anchor"&gt;&lt;/a&gt;The &amp;ldquo;Open Source&amp;rdquo; Mistake
&lt;/h3&gt;&lt;p&gt;One of the clearest examples of AI needing oversight: in our first blog post, Claude described the project&amp;rsquo;s code as &amp;ldquo;open source.&amp;rdquo; It&amp;rsquo;s not. This is a commercial game that will be monetized. Claude defaulted to &amp;ldquo;open source&amp;rdquo; framing because that&amp;rsquo;s the most common pattern in developer blogs on GitHub.&lt;/p&gt;
&lt;p&gt;The fix was twofold: correct the blog post, and add an explicit rule to CLAUDE.md stating this is a commercial project. Rules like this are how you calibrate AI behavior — not through hoping it gets the context right, but through explicit documentation.&lt;/p&gt;
&lt;h2 id="debugging-patterns"&gt;&lt;a href="#debugging-patterns" class="header-anchor"&gt;&lt;/a&gt;Debugging Patterns
&lt;/h2&gt;&lt;p&gt;Nine days of rapid development produced a collection of debugging patterns — things that went wrong and how we fixed them.&lt;/p&gt;
&lt;h3 id="cdk-and-aws"&gt;&lt;a href="#cdk-and-aws" class="header-anchor"&gt;&lt;/a&gt;CDK and AWS
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;OIDC Token Expiry&lt;/strong&gt;: GitHub Actions OIDC tokens expire after about an hour. If a CDK deployment waits too long for ECS service stabilization (which can happen when health checks fail), the token expires mid-deploy and the stack gets stuck. The fix: use &lt;code&gt;cancel-update-stack&lt;/code&gt; to trigger a faster rollback instead of waiting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ALB Outbound Rules&lt;/strong&gt;: CDK defaults ALB security groups to &lt;code&gt;allowAllOutbound: false&lt;/code&gt;. When Fargate targets are in a different CDK app, health checks time out with &lt;code&gt;Target.Timeout&lt;/code&gt; errors. The fix is explicit: &lt;code&gt;alb.connections.allowToAnyIpv4(ec2.Port.allTraffic())&lt;/code&gt;. This was painful to debug because the ALB appeared healthy — it just couldn&amp;rsquo;t reach the targets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;S3 BucketDeployment Pruning&lt;/strong&gt;: Multiple &lt;code&gt;BucketDeployment&lt;/code&gt; constructs pointing at the same S3 bucket will delete each other&amp;rsquo;s files unless you set &lt;code&gt;prune: false&lt;/code&gt; on all of them. The Flutter web build and the JSON asset definitions were in separate deployments, and one kept deleting the other.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CloudFormation Stack States&lt;/strong&gt;: After a failed deploy, the stack enters &lt;code&gt;UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS&lt;/code&gt;. You cannot deploy again until it reaches &lt;code&gt;UPDATE_ROLLBACK_COMPLETE&lt;/code&gt;. This can take several minutes. We learned to wait rather than repeatedly retry.&lt;/p&gt;
&lt;h3 id="flutter-web"&gt;&lt;a href="#flutter-web" class="header-anchor"&gt;&lt;/a&gt;Flutter Web
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Asset deployment&lt;/strong&gt;: Flutter web packages certain assets (like Material icons) via JSON manifests. If the S3 deployment doesn&amp;rsquo;t include subdirectory JSON assets, icons render as blank squares. This showed up as missing navigation icons in the first deployed build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gesture conflicts&lt;/strong&gt;: The isometric tile maps use tap detection for selecting tiles and pinch-to-zoom for camera control. Flutter&amp;rsquo;s gesture arena system means only one gesture recognizer wins per pointer event. Getting tap, pan, and pinch to coexist required careful configuration of &lt;code&gt;GestureDetector&lt;/code&gt; and &lt;code&gt;InteractiveViewer&lt;/code&gt; — 5 PRs of trial and error.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cache busting&lt;/strong&gt;: The first approach used query parameters (&lt;code&gt;flutter_bootstrap.js?v=timestamp&lt;/code&gt;) but browsers and CDNs handle query-param caching inconsistently. We switched to content-hash file renaming — the build step renames files with their hash, guaranteeing that changed content gets a new URL.&lt;/p&gt;
&lt;h2 id="the-troop-training-ui"&gt;&lt;a href="#the-troop-training-ui" class="header-anchor"&gt;&lt;/a&gt;The Troop Training UI
&lt;/h2&gt;&lt;p&gt;The troop training screen is a good example of iterative design working well. It went through a complete transformation:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://ipjohnson-org.github.io/WorldsOfTheNextRealm.Blog/p/the-development-journey-part-3-what-we-learned/tab-issue.png"
	width="1926"
	height="1468"
	loading="lazy"
	
		alt="Troop training screen with category tabs and wizard character"
	
 
 title="The troop training UI after multiple iterations. Category tabs (Battle, Gathering, Mining, etc.) across the top, a carousel showing the selected troop type with artwork, tier selection, quantity controls, and cost breakdown."
 data-title-escaped="The troop training UI after multiple iterations. Category tabs (Battle, Gathering, Mining, etc.) across the top, a carousel showing the selected troop type with artwork, tier selection, quantity controls, and cost breakdown."
 
	
		class="gallery-image" 
		data-flex-grow="131"
		data-flex-basis="314px"
	
&gt;&lt;/p&gt;
&lt;p&gt;The original design used a dropdown to select troop type and a number input for quantity. Through iteration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The dropdown became category filter tabs (Battle, Gathering, Mining, Farming, Factory, Labor, Magical)&lt;/li&gt;
&lt;li&gt;The number input became a slider with preset buttons (10, 50, 100)&lt;/li&gt;
&lt;li&gt;Troop artwork was added via carousel&lt;/li&gt;
&lt;li&gt;Cost breakdown shows per-unit and total cost&lt;/li&gt;
&lt;li&gt;Training time is calculated dynamically&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each step was a separate PR. None were planned as a sequence — each iteration responded to what the previous version looked like when actually used.&lt;/p&gt;
&lt;h2 id="data-driven-everything"&gt;&lt;a href="#data-driven-everything" class="header-anchor"&gt;&lt;/a&gt;Data-Driven Everything
&lt;/h2&gt;&lt;p&gt;A recurring theme in the later development was replacing hardcoded values with data-driven definitions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ResourceType&lt;/strong&gt;: Started as a Dart enum. Replaced with a string that maps to server-defined resource definitions. This allows adding new resource types without a client update.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BuildingType&lt;/strong&gt;: Same pattern — started as an enum, replaced with data-driven definitions loaded from the server&amp;rsquo;s building definitions endpoint.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Research tracks&lt;/strong&gt;: Originally linear tracks (each research unlocks the next in sequence). Replaced with a tree structure where research nodes can have multiple prerequisites and unlock multiple children.&lt;/p&gt;
&lt;p&gt;The pattern is always the same: start with the simplest thing that works (an enum), discover you need more flexibility, replace with server-defined data. This is pragmatic engineering — you don&amp;rsquo;t build the flexible system until you need the flexibility.&lt;/p&gt;
&lt;h2 id="the-resource-icon-problem"&gt;&lt;a href="#the-resource-icon-problem" class="header-anchor"&gt;&lt;/a&gt;The Resource Icon Problem
&lt;/h2&gt;&lt;p&gt;Even at the end of 9 days, not everything is polished. The resource management screen still has missing icons for most resource types:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://ipjohnson-org.github.io/WorldsOfTheNextRealm.Blog/p/the-development-journey-part-3-what-we-learned/icon-missing.png"
	width="2380"
	height="1672"
	loading="lazy"
	
		alt="Resource screen with missing icons"
	
 
 title="The resource management screen on beta.worldsofthenextrealm.com. Food, Gold Coins, Silver Coins, and a few others have icons. The majority of resources show blank spaces where icons should be."
 data-title-escaped="The resource management screen on beta.worldsofthenextrealm.com. Food, Gold Coins, Silver Coins, and a few others have icons. The majority of resources show blank spaces where icons should be."
 
	
		class="gallery-image" 
		data-flex-grow="142"
		data-flex-basis="341px"
	
&gt;&lt;/p&gt;
&lt;p&gt;The game asset library has thousands of icons, but the mapping from resource definition to icon asset isn&amp;rsquo;t complete. This is the kind of polish work that takes time but isn&amp;rsquo;t blocking — the game is functional, the icons are a visual gap.&lt;/p&gt;
&lt;h2 id="honest-assessment"&gt;&lt;a href="#honest-assessment" class="header-anchor"&gt;&lt;/a&gt;Honest Assessment
&lt;/h2&gt;&lt;p&gt;Nine days is a short time. What we have is a vertical slice — infrastructure to UI, real data flowing through real services, deployed to a real AWS environment. But it&amp;rsquo;s not a game you&amp;rsquo;d want to play yet. Major systems are still stub implementations. Combat doesn&amp;rsquo;t resolve. Guilds aren&amp;rsquo;t functional. The marketplace doesn&amp;rsquo;t exist. The AI companion is a configuration screen with no backend logic.&lt;/p&gt;
&lt;p&gt;The AI-assisted development approach made the foundation phase dramatically faster. Scaffolding 11 repos with CI/CD, CDK stacks, data models, and Flutter screens in under a week would have been months of solo work. But the speed came with a cost — every line needed review, some assumptions were wrong (like the &amp;ldquo;open source&amp;rdquo; framing), and debugging AI-generated code requires understanding code you didn&amp;rsquo;t write.&lt;/p&gt;
&lt;p&gt;The value is real. The risks are real. And we&amp;rsquo;re 9 days in.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="project-wide-stats-feb-8-17-2026"&gt;&lt;a href="#project-wide-stats-feb-8-17-2026" class="header-anchor"&gt;&lt;/a&gt;Project-Wide Stats (Feb 8-17, 2026)
&lt;/h2&gt;&lt;h3 id="development-activity"&gt;&lt;a href="#development-activity" class="header-anchor"&gt;&lt;/a&gt;Development Activity
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Metric&lt;/th&gt;
 &lt;th&gt;Value&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Calendar days&lt;/td&gt;
 &lt;td&gt;9&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Repositories&lt;/td&gt;
 &lt;td&gt;11&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Total commits&lt;/td&gt;
 &lt;td&gt;477&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Total PRs merged&lt;/td&gt;
 &lt;td&gt;237&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Avg PRs per day&lt;/td&gt;
 &lt;td&gt;26&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Most active day&lt;/td&gt;
 &lt;td&gt;Feb 16 (32 PRs)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Avg commits per day&lt;/td&gt;
 &lt;td&gt;53&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="prs-by-repository"&gt;&lt;a href="#prs-by-repository" class="header-anchor"&gt;&lt;/a&gt;PRs by Repository
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Repository&lt;/th&gt;
 &lt;th&gt;PRs&lt;/th&gt;
 &lt;th&gt;Focus&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;FrontEndClient&lt;/td&gt;
 &lt;td&gt;80&lt;/td&gt;
 &lt;td&gt;Flutter game client&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BackendApi&lt;/td&gt;
 &lt;td&gt;40&lt;/td&gt;
 &lt;td&gt;Game API endpoints&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BackendCommon&lt;/td&gt;
 &lt;td&gt;31&lt;/td&gt;
 &lt;td&gt;Shared libraries&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Infra&lt;/td&gt;
 &lt;td&gt;19&lt;/td&gt;
 &lt;td&gt;AWS infrastructure&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Documentation&lt;/td&gt;
 &lt;td&gt;18&lt;/td&gt;
 &lt;td&gt;Design docs, game data&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;OperationalTools&lt;/td&gt;
 &lt;td&gt;17&lt;/td&gt;
 &lt;td&gt;CLI ops tooling&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;AuthenticationService&lt;/td&gt;
 &lt;td&gt;11&lt;/td&gt;
 &lt;td&gt;JWT auth service&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;WorldSimulation&lt;/td&gt;
 &lt;td&gt;9&lt;/td&gt;
 &lt;td&gt;Event processing engine&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;NotificationService&lt;/td&gt;
 &lt;td&gt;8&lt;/td&gt;
 &lt;td&gt;Real-time notifications&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Blog&lt;/td&gt;
 &lt;td&gt;2&lt;/td&gt;
 &lt;td&gt;This site&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Assets&lt;/td&gt;
 &lt;td&gt;2&lt;/td&gt;
 &lt;td&gt;Game artwork&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="codebase-size"&gt;&lt;a href="#codebase-size" class="header-anchor"&gt;&lt;/a&gt;Codebase Size
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Language&lt;/th&gt;
 &lt;th&gt;Files&lt;/th&gt;
 &lt;th&gt;Lines&lt;/th&gt;
 &lt;th&gt;Purpose&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;C#&lt;/td&gt;
 &lt;td&gt;226&lt;/td&gt;
 &lt;td&gt;15,720&lt;/td&gt;
 &lt;td&gt;Backend services, shared libs&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Dart&lt;/td&gt;
 &lt;td&gt;190&lt;/td&gt;
 &lt;td&gt;26,533&lt;/td&gt;
 &lt;td&gt;Flutter game client&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;TypeScript&lt;/td&gt;
 &lt;td&gt;11&lt;/td&gt;
 &lt;td&gt;625&lt;/td&gt;
 &lt;td&gt;CDK infrastructure&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Code subtotal&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;427&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;42,878&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Markdown&lt;/td&gt;
 &lt;td&gt;65+&lt;/td&gt;
 &lt;td&gt;38,000&lt;/td&gt;
 &lt;td&gt;Design docs, game data, READMEs&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;JSON&lt;/td&gt;
 &lt;td&gt;50+&lt;/td&gt;
 &lt;td&gt;20,000&lt;/td&gt;
 &lt;td&gt;Game definitions, config&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;540+&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;100,000+&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="services-deployed"&gt;&lt;a href="#services-deployed" class="header-anchor"&gt;&lt;/a&gt;Services Deployed
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Service&lt;/th&gt;
 &lt;th&gt;Runtime&lt;/th&gt;
 &lt;th&gt;Deployment&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;BackendApi&lt;/td&gt;
 &lt;td&gt;.NET 8 Lambda&lt;/td&gt;
 &lt;td&gt;API Gateway&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;AuthenticationService&lt;/td&gt;
 &lt;td&gt;.NET 8 Lambda&lt;/td&gt;
 &lt;td&gt;API Gateway&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;FrontEndClient&lt;/td&gt;
 &lt;td&gt;Flutter Web&lt;/td&gt;
 &lt;td&gt;S3 + CloudFront&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;NotificationService&lt;/td&gt;
 &lt;td&gt;.NET 8 Fargate&lt;/td&gt;
 &lt;td&gt;ALB&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;WorldSimulation&lt;/td&gt;
 &lt;td&gt;.NET 8 Fargate&lt;/td&gt;
 &lt;td&gt;ECS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="game-features-implemented"&gt;&lt;a href="#game-features-implemented" class="header-anchor"&gt;&lt;/a&gt;Game Features Implemented
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Feature&lt;/th&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Authentication (login, register, JWT refresh)&lt;/td&gt;
 &lt;td&gt;Complete&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;City view with isometric rendering&lt;/td&gt;
 &lt;td&gt;Complete&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;World map with tile data&lt;/td&gt;
 &lt;td&gt;Complete&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Building placement and upgrades&lt;/td&gt;
 &lt;td&gt;Complete&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Resource tracking and definitions&lt;/td&gt;
 &lt;td&gt;Complete&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Troop training UI&lt;/td&gt;
 &lt;td&gt;Complete&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Research tree system&lt;/td&gt;
 &lt;td&gt;Complete&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Management screens (Resources, Buildings, Research)&lt;/td&gt;
 &lt;td&gt;Complete&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;EMF metrics and observability&lt;/td&gt;
 &lt;td&gt;Complete&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Operational tooling (user/data/map management)&lt;/td&gt;
 &lt;td&gt;Complete&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Combat resolution&lt;/td&gt;
 &lt;td&gt;Stub&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Guild system&lt;/td&gt;
 &lt;td&gt;Stub&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Marketplace&lt;/td&gt;
 &lt;td&gt;Stub&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;AI companion logic&lt;/td&gt;
 &lt;td&gt;Stub&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Notification delivery&lt;/td&gt;
 &lt;td&gt;Framework only&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="process-artifacts"&gt;&lt;a href="#process-artifacts" class="header-anchor"&gt;&lt;/a&gt;Process Artifacts
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Artifact&lt;/th&gt;
 &lt;th&gt;Count&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;CLAUDE.md rules documents&lt;/td&gt;
 &lt;td&gt;3 (root, OperationalTools, Blog)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;CI/CD pipelines&lt;/td&gt;
 &lt;td&gt;8&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;NuGet packages maintained&lt;/td&gt;
 &lt;td&gt;3&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Design documents&lt;/td&gt;
 &lt;td&gt;17 (HLD, 10 API LLDs, data model, simulation, notifications, Flutter, auth)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Game definition JSON files&lt;/td&gt;
 &lt;td&gt;20+&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Documented debugging patterns&lt;/td&gt;
 &lt;td&gt;8&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;</description></item></channel></rss>