# City Comparison Project — Bellingham Ingestion & Data Analysis

*Full-text archive of one thread from a longer Claude Code session, 2026-08-28 to 2026-09-10. 10 user turns, 10 assistant turns. Tool calls, tool output, and model reasoning are omitted; every human message and every assistant response is reproduced verbatim.*

*Note: also includes the Bellingham data questions (personnel share of budget, median loaded cost, bargaining-unit coverage) that immediately preceded the macro WAG session.*


---


**Ted:**

https://claude.ai/share/f006a374-f8fc-452e-bbe2-8d619b9292b6

---


**Claude:**

I'll fetch that shared conversation to see what it contains.

The share page didn't render via a plain fetch (it's a JavaScript app). Let me open it in the browser instead.

I read the shared conversation. Here's a recap and my take on where it stands.

**What the conversation covered:** You were exploring the idea of using large public institutions — cities, counties, school boards, special districts — as a fully legible proxy for studying how AI could automate an entire organization, since private companies are opaque to outsiders. The conversation established three main things:

1. **The visibility is real.** Budget books give authorized positions by department and job class, salary transparency databases list every employee, civil service class specs document duties in detail, union contracts spell out work rules, and RFPs force agencies to describe their current systems. A mid-size city is arguably more externally legible than any Fortune 500 company.

2. **The containment problem is a finding, not a flaw.** "The city" isn't a container — it's a graph of ~90,000 overlapping legal entities (municipalities, school districts, counties, ~38,000 special districts) connected by joint powers agreements, MOUs, and service contracts. The suggested fix was to make the unit of analysis a *service* ("emergency response for this geography") rather than an organization, and to model the entity graph first. The seams between entities are themselves a large, disproportionately automatable category of work.

3. **The lens is genuinely vacant.** The govtech AI wave is all wedge products (permitting, procurement, FOIA, 311), consultants do this behind paywalls client-by-client, and academic exposure studies work at the economy-wide occupation level. Nobody has publicly modeled one whole real organization end to end. The conversation ended by sketching the missing artifact: "City X, all 2,400 FTEs, every job class scored against current model capabilities, with the entity graph and interagency seams mapped."

**My take:** the ending is right that this artifact doesn't exist publicly, and it's also the kind of project that's unusually well-suited to doing *here* — it's mostly document gathering, extraction into structured data, and scoring, which is exactly what an agentic coding session is good at. The concrete first steps would be: (a) pick a candidate city in the 100k–300k range, ideally in a state with strong salary transparency (California via Transparent California is the easiest); (b) pull its adopted budget book, payroll data, and class spec library; (c) build the position/class dataset and the surrounding entity graph.

You didn't include an instruction with the link, so before I start pulling documents: do you want me to kick off this research program (I'd start by proposing 3–4 candidate cities with a note on how good their public data is), or did you share this for a different purpose — like saving the ideas to a project folder or turning the conversation into a document?

---


**Ted:**

@"/Users/tedpeters/Documents/Projects/Claude Projects/City Comparison/"
Yes, can you propose some candidate cities?  Probably ideally in the west coast, most familiar to me.  washington state best, but best data of course overrules.  also colorado or iowa if those track

---


**Claude:**

That folder already has some prior work in it — let me read what's there before proposing candidates, so I don't duplicate or contradict it.

I've launched three parallel research agents to verify actual data availability (not just guess from memory) for candidate cities in each region you named:

- **Washington:** Bellingham, Everett, Olympia, Spokane Valley, Vancouver, Kent — checking budget-book FTE detail, salary data coverage, class specs, CBAs, and whether the State Auditor's FIT tool gives standardized financials.
- **California** (as the data-quality benchmark, since Transparent California + the State Controller's payroll site are the best in the country): Santa Rosa, Ventura, Santa Cruz, Redding, Roseville.
- **Colorado & Iowa:** Fort Collins, Boulder, Greeley, Cedar Rapids, Iowa City, Des Moines.

One thing worth flagging while they work: your existing schema targets small cities (1k–25k, Blaine-sized), while the shared conversation's automation-lens argument pointed at 100k–300k — big enough to have real bureaucratic structure (civil service classes, multiple CBAs, dozens of departments) but small enough to fully enumerate. The candidates above lean toward that larger band. There's also an obvious synergy candidate: **Bellingham** is Blaine's regional center, so modeling it would extend the Whatcom County entity graph you've already started mapping rather than starting cold.

I'll synthesize a ranked proposal with verified sources once the recon comes back.

The Washington agent stopped early with only a status note instead of its full report. Let me nudge it to finish.

The Washington recon agent needed a nudge — it had paused mid-work. It's now resumed and will deliver the full report. Still waiting on all three regional recon reports; I'll synthesize the ranked candidate list once they're in.

The Bellingham deep-dive just came back, and it's a strong result: the 2026 adopted budget has a "Budgeted Positions" chapter listing every job title with FTE counts and bargaining unit per department, the city hosts its own library of ~400 job-description PDFs, all nine union contracts are posted, and name-level 2025 payroll exists on aggregator sites (though those block automated fetching). The one structural caveat: a proposed Regional Fire Authority could move ~220 fire FTEs off the city's books around 2027.

Still waiting on the full Washington report (other five cities), plus the California and Colorado/Iowa recon. I'll synthesize the ranked proposal once they're all in.

The full Washington report is in — it's strong, with all six cities verified. Quick highlights before the other two regions land:

- **Everett** ranked first: its budget personnel tables, 410-spec class library, and salary ordinance all share the same occupation codes — a clean three-way join — plus six posted union contracts and a real open-data portal. It also has an unusually broad in-house job mix (its own transit system, a huge regional water utility, fire, courts, library).
- **Bellingham** ranked second: the cleanest all-first-party document stack (title-level FTEs with union mapping, ~400 job-description PDFs, all nine CBAs), plus the best local newsroom of the six (Cascadia Daily News).
- **Spokane Valley** is a fascinating structural outlier — a 107k-population city run by ~118 employees, with police, courts, jail, fire, and library all provided by other entities under contract. Weak as a primary subject, ideal as the contract-city comparator.
- **Kent** was the disappointment: its budget PDF is image-only scans and its website blocks scrapers.
- Statewide: the Auditor's FIT tool gives standardized financials for every WA local government, but Washington has no statewide salary database — name-level pay comes from aggregator sites built on public-records requests.

Waiting on California and Colorado/Iowa now; then I'll write up the ranked cross-state proposal.

California's report is in as well, and it confirms California as the data-quality ceiling: a mandatory statewide compensation database (bulk CSV, 2009–2025, every city/county/special district), publicly posted union MOUs as routine practice, and LAFCO municipal service reviews that literally map the entity graph. **Santa Rosa** ranked first there — it publishes a standalone 14-page document listing every authorized position by classification, FTE, salary step, and bargaining unit, which is exactly the crown-jewel artifact for this project. **Roseville** is a strong second, with the richest entity graph (city-owned electric, water, wastewater, and solid waste utilities on one payroll).

One region left — Colorado/Iowa. Once that lands I'll write the full cross-state synthesis and recommendation.

All three regional recon reports are in. Let me save the full findings to your City Comparison folder, then give you the ranked proposal.

All three recon reports are in, verified against live documents, and saved to [candidate-cities-recon.md](../Claude Projects/City Comparison/candidate-cities-recon.md) in your City Comparison folder. Here's the proposal.

**The good news: Washington holds up.** You don't have to trade familiarity for data quality — two WA cities verified at job-title-level position data, competitive with the best in the country at the city-document level.

**My two finalists, both Washington:**

1. **Everett (~113k)** — best data *mechanics* anywhere in the recon. Its budget personnel tables, its 410-spec class library on NEOGOV, and its salary ordinance all share the same 4-digit occupation codes — a clean three-way join of position → duty statement → pay, which is most of the model's skeleton for free. It also has the broadest in-house job mix of any city checked: its own transit system, a ~250-FTE regional water utility, fire, courts, and library all on one payroll, plus a real open-data portal.

2. **Bellingham (~93k)** — best *ecosystem*, and the research-design synergy. The 2026 budget has a chapter listing every job title with FTEs and bargaining unit, the city hosts ~400 job-description PDFs itself, all nine union contracts are posted, and Cascadia Daily News is the strongest local newsroom of any city surveyed. Crucially, it's Blaine's regional center — you'd have a 6k city and a 93k city as two specimens on the same Whatcom County entity graph you've already started mapping.

**One comparator worth adding regardless: Spokane Valley (~107k)** — a city of 107,000 run by ~118 employees, with police, courts, jail, fire, and library all delivered by other entities under contract (documented in its own budget book, with an 11-year FTE series). It's the contract-city natural experiment: scoring it against a full-service peer measures how much of a "city" is really contract administration — the seams insight from your shared conversation, made quantifiable.

**On the other states:** California is confirmed as the strongest *state* infrastructure — mandatory position-level compensation CSVs for every local government (2009–2025), routinely posted union MOUs, and LAFCO service reviews that map the entity graph by statute. **Santa Rosa** publishes the single best document found anywhere: a standalone 14-page list of every authorized position by classification, FTE, salary step, and bargaining unit. **Des Moines** was the sleeper — its budget has exactly the classification-level personnel tables you want. Colorado underperformed: Fort Collins publishes its own per-position pay datasets but has no central duty-statement library, and its budget is organized by "offers" rather than classifications. I'd hold CA (Santa Rosa or utility-rich Roseville) as the second-state transferability test rather than the starting point.

Practical caveats: the salary aggregator sites (GovSalaries etc.) bot-block automated fetching everywhere, so name-level pay needs browser-based collection; and both WA finalists have a restructuring in flight (Bellingham's regional fire authority vote in early 2027 could move ~220 FTEs off the city's books; Everett's transit merger talks could move ~160) — annoying for longitudinal comparison, but arguably a feature, since you'd catch an entity-graph mutation live.

If you tell me Everett or Bellingham (or both), the natural next step is Phase 1 ingestion: pull the budget's position tables and the class-spec library into structured data and stand up the position→duties→pay join.

A late-arriving deep-dive on Kent came in after I'd written up the synthesis. It softens the "skip" verdict slightly, so let me fold the corrections into the saved recon file.

The Kent deep-dive finished late and I've updated the recon file with it. It upgrades Kent from "skip" to "workable but high-friction": the budget's appendix does contain the full job-class universe (~750+ titles with salary steps across six salary schedules), four union contracts are posted, and its entity graph is more interesting than expected — fire left for a regional authority in 2010, yet Kent runs its own municipal court and the largest municipal jail in the Northwest, plus co-owned 911 and housing interlocals. But the core frictions stand: the budget PDF has no text layer (OCR required), FTE counts stop at division level, and its class-spec library died in a Workday migration.

None of this changes the recommendation — Everett and Bellingham remain the two finalists, with Spokane Valley as the contract-city comparator. Ready to start Phase 1 ingestion on whichever you pick.

---


**Ted:**

Seems like Bellingham is the one to start with, as I will be able to sanity check much of it from memory, so let's go ahead with phase 1 ingestion on bellingham, and then depending on how that goes, everett and spokane valley would round out a nice 3 city comparison to validate what we're doing

---


**Claude:**

Bellingham it is — and the sanity-check-from-memory point is a real advantage. Let me set up Phase 1 ingestion: budget position tables → structured data, the class-spec library → duty statements, pay schedules + CBAs, then the position→duties→pay join.

Budget PDF downloaded (2.6 MB, real PDF). Now let me locate the Budgeted Positions chapter and inspect its table layout.

Good progress — 361 position rows, but a few departments double-count (likely duplicated text layers) and single-value rows need column-aware assignment. Let me debug.

Down to one 0.1 discrepancy in Parks. Let me look at the raw page to find it.

The citywide history page is clean (2026 total 1,120.4 FTE matches the recon). Let me add a history parser and a citywide validation to the script.

The index page has the full table server-side (354 PDF links). Writing the scraper:

Relative URLs — quick fix:

All 352 duty-statement PDFs downloaded with zero errors. Quick quality check on the extracted text:

22 pay schedules and 59 labor documents down; 5 came back as HTML (likely dead links). Let me check what happened with those:

Now the matcher improvements — Classification column as alternate title, broader level-stripping, plural normalization:

Matching jumped to 64% of rows / 50% of FTEs. The big remaining block is uniformed police/fire. Let me check whether those specs exist in the library at all:

Good progress. Before finalizing, one targeted check on the big civilian misses — are they truly absent from the library or hiding under other names?

Plan C lost Captain/Inspector to an over-eager stop-word, and two plans (dept-head, librarian) parse empty. Inspecting those two formats:

Plan E (dept heads) uses "Title $amount" lines — needs a small special case. The librarian PDF returned no text at all; let me check if it's a scan and whether the 2025 or 2027 version has text:

No text or image objects at the top level — likely inline-image scans. Easier route: the librarians' 2025–2027 CBA should contain the same salary appendix. Checking:

The CBA has the full 2026 librarian table. Applying the remaining fixes — Plan C stop-word, dept-head parser, Plan L from the CBA, stray-year cleanup:

Plan E-DH caught most dept heads but dropped Deputy Police Chief — quick debug:

The dept-head sheet is now fully legible and small — hand-encoding it beats more heuristics:

Now wiring pay into the join:

73% pay coverage. The biggest misses are compound budget titles ("Firefighters,FF/Paramedics" = 132.3 FTE) that need component-wise matching, and parentheticals. Fixing:

Two notable titles miss only on word order/plural ("Chief of Police" vs "Police Chief"; "Fire Inspectors" vs Plan C's "Inspector"). Small alias map, then the report:

Phase 1 ingestion for Bellingham is complete. Everything is in your City Comparison folder — full details in [phase1-ingestion-report.md](../Claude Projects/City Comparison/bellingham/phase1-ingestion-report.md), with re-runnable parsers in `tools/`.

**What you now have:**
- **Every budgeted position**: 344 classification rows across 15 departments (title, FTE for 2025-revised and 2026-adopted, bargaining unit), validated against the budget's own printed subtotals, plus the citywide FTE history by employee group back to 2017.
- **All 352 duty-statement PDFs** from the city's job-description library, with text extracted and indexed by department, unit, classification, and salary grade.
- **All 12 pay plans parsed** into a title → grade → step-schedule table (389 titles), including the formula-based ones (Battalion Chiefs are defined as 8–12% over top-step Captain; Police Lieutenants as 18% over top-step Sergeant).
- **The join** ([positions_joined.csv](../Claude Projects/City Comparison/bellingham/data/positions_joined.csv)): **91% of the city's 1,121.7 FTEs have salary structure attached**; 55% also have full duty statements.

**The findings worth knowing about** (things you may be able to sanity-check from memory):
1. **The JD gap is structural.** The public library has no specs for uniformed civil-service ranks — no Patrol Officer, Sergeant, fire Captain, Battalion Chief, or Dispatcher — and it's thin on trades (Plant Operator, Fleet Mechanic). Those live with the Civil Service Commission, so a records request is the clean path to 100% duty coverage. Their *pay* is fully covered regardless.
2. **The budget book disagrees with itself** in small ways: Parks & Rec's position rows sum to 103.7/102.3 FTE but its printed subtotal says 103.8/102.2, and citywide the department tables total 1,073.9 regular FTEs for 2026 while the history page says 1,073.1. Tiny, but it tells you the city's own position-control and budget documents aren't perfectly reconciled.
3. **Five labor-agreement links on the city's HR page are dead 404s**, and all three posted librarian pay schedules are image-only scans (I pulled those values from the CBA appendix instead).
4. About half the budget's position pages render text as per-character runs that defeat normal PDF extraction — the parser rebuilds lines from character coordinates, and that trick should transfer to other cob.org documents.

**Natural next steps** when you're ready: a browser-based pull of the name-level payroll (GovSalaries has 1,321 employees for 2025 but blocks scripts) to reconcile budgeted vs. filled positions, then the same Phase 1 treatment for Everett — its occupation codes should make the join nearly automatic — and Spokane Valley as the contract-city comparison.

---


**Ted:**

so we're pretty well covered on jobs, pay, responsibilities/descriptions, yes?  How else should be looking at the city as organization?  Do we know about the orgs it is connected to?  anything else about process?  I don't know what I don't know, but it seems like we want to pivot around this to get a full view of the organization

---


**Claude:**

Short answer: yes on the roster — jobs, pay, and formal responsibilities are now solid. But what we have is the org's *anatomy*. A full organizational view needs about six more layers, and the good news is most of them have concrete, already-located sources. Here's the map, roughly in order of how much they'd tell us per unit of effort:

**1. Money flow (the easiest big win — it's in a document we already have).** We parsed ~20 pages of a 199-page budget book. The rest is the org's metabolism: fund structure (general fund vs. utility enterprise funds), revenues by source, department operating budgets, and the capital program. Work follows money — Blaine taught you that (60% of spend was utilities). It also usually contains department narratives with goals, accomplishments, and sometimes workload metrics. Behind it sit the ACFR and the State Auditor's FIT data (standardized BARS categories, ~10-year history, machine-readable) for time series and peer comparison.

**2. The service catalog (the pivot you're looking for).** The shared conversation's key advice was to make the *service*, not the org, the unit of analysis. Right now we have positions grouped by department; the reframe is an inventory of maybe 40–60 services the city delivers — drinking water, permitting, patrol, adjudication, book lending — each mapped to: delivering department → positions/classes (our data) → funding fund (layer 1) → statutory mandate → any external orgs involved. That's the join table that turns a roster into an organization. Automation exposure scores make much more sense attached to services than to job titles, because one service's workflow crosses several classes.

**3. The entity graph (partially recon'd, not yet built).** We know the nodes from the recon — Whatcom County, Bellingham School District, Port of Bellingham, WTA, PUD #1, Fire District 8 and the pending RFA. What we don't have is the *edges*: Bellingham is unusual in that it runs regional infrastructure for other governments — What-Comm dispatch and Prospect fire/EMS dispatch serve agencies county-wide, and Medic One runs county-wide on a county EMS levy. Three concrete sources: interlocal agreements (Washington's RCW 39.34 requires them to be filed or posted publicly), the intergovernmental revenue/expenditure lines in the budget (every one is an edge with a dollar weight), and the ACFR's component-unit and related-party notes. This is where the "seams are disproportionately automatable" thesis becomes testable — reconciliation and cost-allocation across those agreements is exactly the administrative overhead the shared conversation predicted.

**4. Decision and process artifacts (the org's event log).** Council agendas and minutes are a complete, dated log of every contract awarded, ordinance passed, and dollar approved — the Bellingham equivalent of Blaine's council-meeting spider, and the CivicPlus patterns partially transfer. Add the municipal code (the org's legal API — what it *must* do), the fee schedule (its product catalog with prices), and RFPs/contracts (what it chooses to outsource, and forced self-descriptions of current processes). The CBAs we already downloaded are process documents too — shift structures, overtime rules, who may do what — we've only used them as pay sources so far.

**5. Workload volumes (the demand side).** Automation exposure is really routineness × volume, and we only have the people side. Volumes exist per service: permits issued (permit center reports), dispatch call volumes, police incidents, court caseloads (WA AOC publishes per-court statistics), library circulation (state library statistics), utility account counts. Where these attach to the service catalog, you can compute transactions-per-FTE — the single most automation-relevant number, and almost nobody assembles it.

**6. The narrative layer.** Cascadia Daily News is Bellingham's Northern Light — the issues, conflicts, and failures that structured data never shows. Plus State Auditor accountability audit findings, which are literally a public log of "where the org's controls broke."

**7. The informal layer (hardest, later).** Budgeted-vs-filled reconciliation (our position data vs. name-level payroll), turnover from year-over-year payroll names, vacancy patterns — the closest an outsider gets to morale and metis. This is where the browser-based GovSalaries pull and eventually records requests come in.

What we can't get without records requests: emails, internal SOPs, the actual (vs. documented) workflows. That's the known floor of outsider legibility — but note that layers 4 and 5 substantially bound it, because minutes + volumes + work rules constrain what the real process can be.

**My recommendation for Phase 2:** do layers 1 and 3 next — parse the remaining budget book (funds, revenues, department narratives) and build the entity graph with dollar-weighted edges from the intergovernmental lines and ILAs. Those two turn "a list of 1,100 employees" into "an organization embedded in a governance network," and they're both mostly in documents already sitting in `raw/`. Then the service catalog (layer 2) as the deliberate modeling step that ties everything together — that one's less parsing and more design, and it's the schema piece that should transfer to Everett and Spokane Valley unchanged. Want me to start on the budget-book deep parse and the entity graph?

---


**Ted:**

I agree, let's wait on the govsalaries pull and do layers 1 and 3.  we'll need some kind of basic visualization of this at some point as well

---


**Claude:**

Phase 2 it is: the full budget-book parse (layer 1) and the entity graph (layer 3), capped with a basic visualization. Let me set up the tasks and start by mapping the budget book's structure.

44 funds parsed with plausible numbers, but the citywide-total page is being mis-attributed to fund 965 and one fund name failed. Checking those pages:

Fund "766" was a stray numeric row misread as a header. Fixing both issues:

Citywide control totals are clean ($544.3M total 2026 expenditures). But sum-of-funds exceeds citywide by $14.7M and REET shows zero expenditures — let me audit that page cluster:

Found the real bug: columns with a trailing "–" (no FY2026 value) shift everything left — the parser was storing FY2025 as FY2026 wherever 2026 was zero. Dashes must be tokenized as zeros:

Got most of the mapping — the first funds are on the previous page. Let me save the full table and inspect a department page:

Department pages are narrative-rich. Let me get the full department TOC listing to segment them:

Department page ranges mapped. Writing the narrative extractor plus the capital-summary parser:

Narratives are solid. The capital table has some scrambled rows — inspecting the actual page:

The capital tables render digits with internal spaces ("2 ,428,000") — I'll extract the numeric region, strip spaces, then tokenize:

The city's page stops at the 2021 ACFR — fine for entity-graph purposes since component-unit structures change slowly. Downloading and extracting the relevant notes:

Confirmed — member-agency payments are booked as charges-for-services (Medic One $14.1M, dispatch $8.9M). Now building the graph file:

Entity graph built — 14 nodes, 15 edges, with roughly $90M/year of documented cross-entity money flow. Now the visualization; let me load the dataviz guidance first, then build.

Palette in hand (the reference palette is pre-validated for adjacent-pair use). Let me quickly confirm the validator passes on the slots I'll use, then generate the aggregate data:

Now writing the visualization — a self-contained HTML page with four views (money by fund, workforce stacks, capital, entity graph), theme-aware, with tooltips and table fallbacks:

Now let me render it and check for layout issues:

The static file snapshot isn't rendering. Let me serve it properly via a dev server:

Top section renders cleanly. Checking the rest of the page:

Blank after scrolling — possibly a JS error killed the later sections. Checking the console:

All sections are populated — the blank was a transient render glitch. Re-screenshotting:

Screenshots only capture the document top in this pane. Workaround: temporarily collapse the earlier sections so each remaining view sits at the top:

The graph works, but Whatcom County's two edges (Medic One $14.1M and shared revenue $5.2M) overlap on the same straight line, hiding one label. Fixing with curved offsets for parallel edges:

Both county edges now render distinctly. One last check — dark mode:

Dark mode validates too. Wrapping up with the Phase 2 report and memory update:

Phase 2 is done — both layers built, validated, and visualized. Full details in [phase2-money-and-entities-report.md](../Claude Projects/City Comparison/bellingham/phase2-money-and-entities-report.md).

**Layer 1 — money.** All 43 funds parsed into [fund_lines.csv](../Claude Projects/City Comparison/bellingham/data/fund_lines.csv) with revenues, expenditures, and reserves (operating and capital separately), plus the capital plan and all 17 department narrative sections. The parse reconciles to the budget's own citywide totals within $20K on $544.3M (0.004%), and the capital summary matches to the dollar. The headline findings: the General Fund is only 24% of the city — utilities and enterprise funds dominate, the same pattern Blaine showed; personnel is ~$174M (~45% of operating); and 2026 plans a $39M reserve drawdown, which is the budget-crisis story from the mayor's message showing up in the numbers.

**Layer 3 — the entity graph.** 14 nodes, 15 edges, dollar-weighted from fund data and the ACFR's interlocal-agreement notes. The interesting discovery: **Bellingham is a net regional service *seller*** — the structural mirror image of Spokane Valley. It runs county-wide ambulance service (~$14.1M/yr in), operates both of the county's 911 centers (~$10.9M/yr from member agencies, with a new $10.5M dispatch building under construction), and sells fire service to Fire District 8 — the exact relationship the 2027 Regional Fire Authority vote would restructure into a new entity. Roughly $30M/yr of city revenue is other governments paying it to run shared services, which makes the "seams are where the automatable overhead lives" thesis directly measurable here.

**The visualization** is at [city-overview.html](../Claude Projects/City Comparison/bellingham/city-overview.html) — fully self-contained, light/dark aware, with hover tooltips and table fallbacks: all 43 funds colored by type, the 1,120 FTEs stacked by bargaining unit per department, capital by department, and the entity graph with edge width proportional to dollars. It's currently viewable in the browser pane (I verified all four views and both themes render correctly; the only fix needed was bowing the two Whatcom County edges apart so both dollar labels show).

One gap worth knowing: the city's website only posts ACFRs through 2021, so the Fire District 8 dollar is a 2021 actual (flagged as such everywhere). The interlocal agreement texts themselves are public records and would put current numbers and governance terms on every edge.

The natural next step is the Layer-2 service catalog — the department narratives are extracted and waiting to be mined into the service → positions → funds → entities join that ties all three phases together. That's also the schema piece that should transfer to Everett unchanged.

---


**Ted:**

hey, I moved this directory, could you update the links?

---


**Claude:**

I moved this directory to `/Users/tedpeters/Documents/Projects/Claude Projects/City Comparison`. Before I change anything, let me find what actually contains hardcoded paths that could now be broken.

The project's own `.md`/`.html`/`.json`/`.py` files have no hardcoded absolute paths — only the `.venv` scripts do (pip bakes the path into their shebangs), which means the virtualenv is likely broken by the move. Let me confirm what "links" are actually affected: the venv, plus any markdown links inside the docs.

Venv still works and the docs contain no markdown/path links between them — earlier parsers all used relative paths, so those are fine. The things that were tied to the *old* session location are what could be stale: the HTML files' internal references, the `launch.json` I created (in the old Quicken project folder), and the project memory (written under the old session's derived path). Let me check those.

That clarifies it. Nothing physically broke — the venv works, the absolute path is unchanged, and your docs/HTML use relative paths internally. What changed is the **session's working directory**: it's now the City Comparison folder itself, whereas earlier the session ran from the Quicken folder and I gave you file links prefixed with `../Claude Projects/City Comparison/`. Those clickable links now point to the wrong place. Let me confirm the files so I hand you correct ones.

Done — all eight files confirmed present, and nothing was actually broken by the move: the venv still runs, and your docs, parsers, and the HTML all use relative paths internally. The only thing that went stale is the clickable file links I'd given you, which were written relative to the old working directory (the Quicken folder, hence the `../Claude Projects/City Comparison/` prefix). Corrected to the new working directory, they are:

**Current work**
- [candidate-cities-recon.md](candidate-cities-recon.md) — ranked candidate cities across WA/CA/CO/IA
- [bellingham/phase1-ingestion-report.md](bellingham/phase1-ingestion-report.md) — workforce: positions, specs, pay
- [bellingham/phase2-money-and-entities-report.md](bellingham/phase2-money-and-entities-report.md) — funds + entity graph
- [bellingham/city-overview.html](bellingham/city-overview.html) — the visualization

**Blaine specimen (earlier)**
- [blaine-wa-profile.md](blaine-wa-profile.md) · [blaine-data-source-recon.md](blaine-data-source-recon.md) · [city-schema-v0.1.md](city-schema-v0.1.md) · [city-as-machine.html](city-as-machine.html)

One genuinely orphaned thing I noticed while checking, not a link but worth flagging: the project's **memory** (`city-comparison-project.md` and its `MEMORY.md` pointer) still lives under the *old* session's derived memory path (`…-Codex-Projects-Quicken-and-Simple-Fin/memory/`). Because this session now runs from the City Comparison directory, it derives a different, currently-empty memory path — so a future session started here won't automatically load the project's memory. If you'd like, I can copy those two City-Comparison memory entries over to this project's memory location (leaving the TPC/Quicken memories where they belong). Want me to do that?

---


**Ted:**

yes thanks

---


**Claude:**

Got both. I'll copy the City Comparison project memory into this project's memory location and give it a fresh `MEMORY.md` index containing only that entry — the TPC/Quicken memories stay where they are.

Now I'll clean up the old location so the misfiled entry doesn't keep loading in the Quicken project — remove its pointer line from the old index and delete the old copy (its content is now safely in the new location).

Done. The project's memory now lives with the project:

- **New location** (`…-Claude-Projects-City-Comparison/memory/`) holds `city-comparison-project.md` plus a fresh `MEMORY.md` index pointing to it — so any future session started from this directory will load the project's context automatically. I also updated the note in it to reflect that City Comparison is now its own project root (Phase 1+2 both done).
- **Old Quicken location** keeps its four TPC memories untouched; I removed only the misfiled City Comparison entry (both the file and its index line).

So both the stale file links and the orphaned memory are sorted, and the two projects' memories are now cleanly separated.

---


**Ted:**

do we know how much of the budget goes to employees, the 1120.4 FTEs?

---


**Claude:**

Good question — let me compute it precisely from the parsed fund data rather than rely on the rough figure in the report.

About **$171.0M goes to the 1,120.4 FTEs** in 2026 — $124.7M in salaries and wages plus $46.3M in benefits. But the share of "the budget" that represents depends entirely on which denominator you use, and that spread is itself the interesting part:

| Denominator | Amount | Personnel share |
|---|---|---|
| Total all-funds budget | $544.2M | **31%** |
| Operating expenditures only | $388.4M | **44%** |
| General Fund only | $129.0M | **68%** |

The all-funds 31% is misleadingly low — that denominator is padded with $155.9M of capital construction, debt service, and internal-fund recharges that aren't about staff. The **44% of operating** spend is the fair citywide figure. And in the **General Fund — the tax-supported core where police, fire, parks, courts, and admin sit — personnel is over two-thirds** of the budget, which is exactly why the structural imbalance the mayor flagged shows up as staffing pressure: when 68% of your discretionary money is people, closing a gap means either headcount or taxes, and the 2026 budget did both (it cut ~40 positions).

Loaded cost per FTE averages about **$152,600** (≈$111K wages + ≈$41K benefits, a ~37% benefit load) — though that's a citywide mean spanning temp recreation staff to the police chief, so it's a blended figure rather than a typical salary.

One note: this supersedes the "$174.1M / ~45%" rough figure in the Phase 2 report — $171.0M is the precise number from the fund tables (salaries and benefits are booked as their own line items, so summing across funds doesn't double-count payroll even where internal-service staff get recharged to departments). Want me to correct that line in the report?

---


**Ted:**

how about median loaded cost?

---


**Claude:**

Median is the better question here — the mean is dragged up by the top of the scale. I have per-title pay schedules attached to the positions, so I can estimate an FTE-weighted median. Let me compute it, and cross-check it against the known payroll total so the step assumption is grounded rather than guessed.

Median loaded cost is roughly **$145–150K** — and the striking thing is how *close that is to the $153K mean*. Here's what the position-level pay data gives, FTE-weighted across the ~1,018 FTEs (91%) that have a pay schedule attached:

| Step assumption | Median **base wage** | Median **loaded** (+37% benefits) |
|---|---|---|
| Mid-of-range | ~$96,500 | ~$132K |
| Top-of-range | ~$108,600 | ~$149K |

The reconciliation picks the answer for us: if *everyone* sat at the top of their range, base payroll would be $109.2M — yet actual budgeted wages are $124.7M, **14% higher**. So on average staff earn at or above top-of-scale base (overtime, longevity, premium and shift pay, which the schedule excludes and which pile onto fire/police especially). That means the top-step row is the floor, so **median loaded ≈ $149K or a bit more.**

The real headline is that **the median and the mean nearly coincide** (~$149K vs ~$153K). In a private company the mean cost-per-head sits well above the median because a handful of executives distort it; here it barely does, because municipal pay is *compressed* — fixed salary ranges, no equity or bonuses, the police chief's ~$226K base only about 2× the median. So for this org, "average employee costs ~$150K loaded" is genuinely representative, not a statistical artifact.

Two caveats worth stating: it excludes the ~48 temporary-labor FTEs (no salary schedule; low-paid — including them nudges the median down a little), and the 37% benefit load is applied flat, whereas real benefits are partly fixed-dollar (health insurance), which actually loads lower earners *more* than 37% and compresses the loaded distribution even further toward that median.

---


**Ted:**

what's the percentage with a bargaining unit (union or guild) and is it mandatory for their positions.

---


**Claude:**

Let me compute the represented share from the position data, then address the "mandatory" part, which has an important legal nuance.

**85% of the workforce is in a bargaining unit** — 957 of the 1,121 active FTEs sit in one of nine union/guild units. Excluding temporary labor (which has no schedule and isn't organized), it's **89% of the benefited workforce**, matching the "~85–90%" the recon flagged. The breakdown:

| Unit | FTE | Who |
|---|---|---|
| Local 1937 (Guild of Pacific NW Employees) | 422.9 | general non-uniformed |
| IAFF 106 | 182.3 | firefighters/paramedics |
| Teamster 231 | 155.0 | supervisors & professionals |
| Police Guild | 121.0 | uniformed police |
| W-C Disp Guild | 36.0 | police (What-Comm) dispatchers |
| AFSCME 114F | 16.0 | fire/EMS dispatchers |
| IAFF 106S | 10.0 | fire staff officers |
| AFSCME 114L | 8.0 | librarians |
| FOP | 6.0 | police lieutenants |

The remaining ~15% is **non-represented** (105.7 FTE of "E-Team" — department heads, confidential/managerial staff, certain at-will professionals — plus 10 elected) and **temporary labor** (47.8 FTE, ~4%).

**On "mandatory," there are two separate things, and the answer is different for each:**

1. **The position's coverage is not optional.** Whether a job is in a bargaining unit is a function of its *classification*, not the employee's choice or the city's discretion. HR (and the Civil Service Commission for classified positions) allocates each job class to a unit; the budget's own note carves out the E-Team as "not represented… exempt from Civil Service" (elected officials, department heads, certain professionals, confidential/managerial staff — the legally excludable categories under Washington's public-employee bargaining law, RCW 41.56, administered by PERC). Everything else falls into a unit and is covered by that unit's CBA — wages, hours, and working conditions are governed by the contract, and the union is the *exclusive* representative. You can't individually opt your position out.

2. **But union *membership* is voluntary and legally cannot be compelled.** Since *Janus v. AFSCME* (2018), a public employer cannot require an employee to join the union or pay agency/"fair-share" fees as a condition of employment. So an employee in a represented position can decline to be a dues-paying member — yet is still covered by the CBA and represented by the union regardless (they get the contract's pay scale, benefits, and grievance rights either way). Any older "union-shop" language in a CBA is unenforceable on that point for public employees.

So: **coverage by a bargaining unit is essentially mandatory for the ~85% of positions whose classifications fall in a unit; actually belonging to and paying the union is not.** If it's useful, I have all nine CBAs downloaded in `bellingham/raw/cbas/` and could pull each unit's specific membership/dues-checkout language to confirm how each contract words it post-Janus.

---
