Quartet / Quintet

2026-09-05  ·  debugging · githubactions · webdev · softwaredevelopment

Bisection Narrowed It Correctly and Still Never Reached the Answer

I built a pipeline that publishes articles to Zenn by git push, using its GitHub integration. I pushed twelve at once. Two went live. The other ten did nothing.

No errors. The integration was fine. The push arrived. The deploy reported success. And the articles did not appear.

It took me a full day to find the cause. The cause itself was not difficult. What was difficult is that the reason for the failure was never returned to me.

What I tried first (all wrong)

My first assumption was that one file was malformed.

Tried Result
Confirm the push arrived It did
BOM / CRLF / control characters Clean. Identical in shape to the articles that worked
Diff the frontmatter No difference
Empty commit, redeploy No change
Change the body, redeploy No change
Rename the slug (twice) No change
Wait 4.5 hours, push a single file No change
Push a brand new article the platform had never stored No change

That last row killed the hypothesis. A brand new file was not saved either, so this was not about any individual file.

Half a day gone, spent knocking down guesses one at a time.

Switching to bisection

I stopped guessing and started cutting the input in half.

The repository held twelve articles plus a book (ten chapters and a config). I deleted the book outright.

Then I deleted articles too, down to the two live ones plus one candidate — three files total.

That yields a conclusion:

The repository contents are not the cause.

This was a correct conclusion. Bisection worked. It killed hypotheses far faster than the exhaustive approach that preceded it.

And it still did not reach the answer.

Why it could not

Any experiment you run locally reduces to: change the input, observe the output. Bisection is one such method — halve the input range, watch the output change.

It assumes the output is observable.

In my case the output was a single bit: the article appeared, or it did not. And the actual reason for the failure is not returned in the push response, not exposed through the API, and not written anywhere in the repository. It is displayed only on Zenn's deploy dashboard.

Which is to say: I had never once looked at the place where the reason lived.

However far you narrow the input, if the output stays one bit wide, one bit is all you get. When the reason sits somewhere unobservable, local experiments cannot reach it in principle.

The actual reason

The deploy dashboard said:

The following articles were not deployed because the posting limit was reached: (four article names)

A posting rate limit. The deploy had succeeded every single time; the articles were rejected inside it.

Zenn's FAQ states:

Measured: two got through in 24 hours, and everything after that was dropped.

The very first thing I did — pushing twelve at once — was the cause.

I had the order backwards

The mistake was not using bisection. It was the order.

What I should have done, before starting to narrow anything, is find out where the failure reason is reported.

That check takes five minutes. Instead I spent half a day staring at an output that was never going to say anything.

"No error appeared" is not the same as working. It may only mean you are not looking where the errors appear. Those two states are entirely different.

I fell into the same hole again the same day

On dev.to, updating nine published articles through the API. Spaced three seconds apart, three of them failed with 500 Internal Server Error.

500 is a server-side fault. Read plainly, it means the other end broke. I waited ten seconds and retried — all three succeeded.

The rate limit was arriving as a 500. Not a 429.

The error you get back does not necessarily describe what happened. "A 500 came back" means a 500 came back. It does not mean the server broke.

Automation makes you look like a spammer without meaning to

One more thing, and it is not technical.

Publishing twelve articles at once is physically impossible for a person. From the platform's side, that shape is indistinguishable from a machine flooding the queue.

Zenn does have a policy on AI-written content:

My articles are built from bugs I actually hit and numbers I actually measured. The substance is on the right side of that policy. The delivery was not.

The rate limit is better understood as a warning than a punishment. Which means looking for a workaround is the wrong move. Slowing down is the correct fix.

What I changed

Keep exactly one article queued for publication at a time

That is the whole fix. When one goes live, queue the next.

An automated pipeline, left alone, will try to emit everything it is holding. The limit had to come from what is acceptable to publish, not from what is available to publish.

Summary

The bisection I spent half a day on reached a correct conclusion. One line of a log would have finished it in five minutes.


Related


I publish the configuration for splitting Claude Code into separate personas — Architect, Coder, Reviewer, Conflict Resolver — under MIT. Copy it, run ./setup.sh, and it works. It does not depend on your tech stack.

https://github.com/quintetkit/quartet

I built one real tool using nothing but this workflow. Every Issue, PR, review and merge is still there. The parts that went wrong were not deleted.

https://github.com/quintetkit/mdlinkcheck

The version that adds a UI Designer persona, review criteria, a per-Issue parallel execution script and a 10-chapter guide is on the product page.

The full kit — five personas, the scripts and the complete guide in English and Japanese — is on BOOTH, a Japanese store with an English interface that takes international cards.

https://quartet-dev.booth.pm/items/8807156

The workflow itself is available

Quartet, the four-persona version, is published free under MIT. Quintet adds a UI Designer persona, review criteria, a per-Issue parallel execution script, and a 10-chapter guide.

See the free version Product page