A buffered channel bounds the jobs sitting in it. It does nothing about the goroutines blocked trying to send.
Say a server gets a burst of 100 requests, one job each, with four workers and a channel that holds eight. Nothing finishes during the burst. Four jobs run and eight sit in the buffer. If each request already launched a goroutine to do the send, the other 88 are parked on that send. The queue has a bound. The crowd outside it has none.
Moving the send into yet another goroutine only moves the wait. An unbuffered channel makes sender and receiver meet at the exchange, but it doesn't cap how many senders arrive.
Admission goes at the request entry point. Get a place before launching work, or return saturation right there. If admission waits instead, that wait needs its own population cap and a deadline, or the queue has just moved upstream.
The handler signals cancellation when the caller leaves, and workers check it before starting queued work and at useful stopping points. The signal doesn't free a place, though. A job keeps counting until it really stops occupying the resource being limited.
For a fixed batch of twelve small jobs I'd just launch twelve goroutines. With a fixed input size, a pool may buy very little.