Colby McHenry bd86ad2061 test(explore): #1500 regression fixtures for budget allocation (CG-6) 1 ماه پیش
..
cmd bd86ad2061 test(explore): #1500 regression fixtures for budget allocation (CG-6) 1 ماه پیش
internal bd86ad2061 test(explore): #1500 regression fixtures for budget allocation (CG-6) 1 ماه پیش
README.md bd86ad2061 test(explore): #1500 regression fixtures for budget allocation (CG-6) 1 ماه پیش
go.mod bd86ad2061 test(explore): #1500 regression fixtures for budget allocation (CG-6) 1 ماه پیش

README.md

payroll-go — the #1500 regression fixture

A synthetic Go service reproducing the repo shape from issue #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 runPayrollCycleAllBuildPayslipUpsert, 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

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.