# payroll-go — the #1500 regression fixture A synthetic Go service reproducing the repo shape from [issue #1500](https://github.com/colbymchenry/codegraph/issues/1500): **generated CRUD sitting beside the hand-written use-case that does the real work.** This tree is a fixture, not a program. It never compiles or runs — it exists to be indexed. Keep it valid, idiomatic Go anyway: the extractor's output is the whole point. ## The shape ``` cmd/payrolld/main.go wires the service internal/transport/httpapi/ HTTP entry point → use-case internal/usecase/payroll/ ← THE ANSWER. Hand-written workflow: cycle.go runPayrollCycleAll (227 lines) payslip_builder.go BuildPayslip — the actual pay calculation prorate.go internal/domain/payroll/payslip.go hand-written domain types internal/store/payslipstore/store.go the real Upsert internal/platform/clock/clock.go internal/gen/fkit/payroll/ ← THE NOISE. Generated CRUD, ORDINARY names: payslip.go CreatePayslip, GetPayslip, UpdatePayslip, a second BuildPayslip payroll_cycle.go CreatePayrollCycle, PayrollCycleCreateRequest, … store.go a second Upsert calculate.go CalculatePayrollCycleTotals, CalculatePayslipNet, … dto.go internal/gen/fkit/employee/, timesheet/ more generated CRUD internal/gen/payrollpb/*.pb.go generated, detectable by PATH ``` The chain the fixture is built around is `runPayrollCycleAll` → `BuildPayslip` → `Upsert`, entered from `POST /v1/payroll/cycles/{cycleID}/run`. ## The three properties that make it a regression fixture 1. **Generated files that only a CONTENT header betrays.** The `internal/gen/fkit/**` files have ordinary names (`payslip.go`, `store.go`) and carry `// Code generated by fkit v3.11.0. DO NOT EDIT.`. Path-only detection misses every one of them — that is the #1500 case, and why CG-5 added the content check. The `payrollpb/*.pb.go` files cover the path-detectable channel beside them. 2. **Deliberate name collisions.** `BuildPayslip`, `Upsert` and `Store` each exist twice, once generated and once hand-written. The generated layer also name-collides on every term of the question below — `CreatePayslip`, `PayrollCycleCreateRequest`, `CalculatePayrollCycleTotals` — so a scorer that rewards incidental name matches surfaces the CRUD path. 3. **A size split that drives the render mode.** `cycle.go` is deliberately over the whole-file window (227 lines) so it falls through to clipped clusters; the generated files are deliberately under it so they ship whole. Allocation follows file size, not relevance. `__tests__/explore-allocation-1500.test.ts` pins both sides of that split — if you edit these files, keep it. ## The assertion Query: **"how does payroll cycle create and calculate payslips?"** — an architecture question that names none of the symbols that answer it. The budget should concentrate on the hand-written workflow. As of 2026-08-03 it does not: | | allocated | delivered | |---|---|---| | hand-written | 48.4% | **25.6%** (all of it domain types) | | generated CRUD | 39.9% | **57.4%** | `cycle.go` is allocated the single largest slice (7,052 chars, 30.6%) and delivers **zero** — the hard ceiling drops its whole section. `payslip_builder.go` (rank #8) never renders at all. `runPayrollCycleAll`, the hand-written `BuildPayslip` and the real `Upsert` never reach the agent. ## Running it ```bash npm run build node scripts/agent-eval/probe-allocation.mjs payroll-go # exits 1 today, by design npx vitest run __tests__/explore-allocation-1500.test.ts # green today, by design ``` The probe reports per-file budget share against `scripts/agent-eval/allocation-fixtures.json`. The vitest suite pins the fixture's shape and holds the allocation assertion as `it.fails` — green while the bug is open, red the moment it is fixed. See `docs/design/explore-budget-allocation.md`. ## Known finding: the chain's `Upsert` edge resolves to the generated store `runPayrollCycleAll` calls `s.store.Upsert(ctx, slip)`, where `s.store` is a `*payslipstore.Store`. The graph resolves that edge to `internal/gen/fkit/payroll/store.go` — the **generated** `Store.Upsert` — not the hand-written one. Same-name method resolution across two packages that both define `Store.Upsert` picks the wrong receiver. This is a resolution defect, not a budget one, and it is left unfixed on purpose: it is upstream of the allocation bug (a wrong edge pulls the generated store into the subgraph and inflates its score), so it belongs with the scoring work in CG-10 rather than here. The test asserts only that the workflow reaches *an* `Upsert`, so tightening the resolver later will not break the fixture.