Pin twelve query rows before any helper moves. A coding model can draft a cleaner function quickly.
The safe gate is still a frozen output table. Move one rule only after that table stays green.
The conclusion in one pass
Do not extract a query normalizer as a whole. Freeze current outputs, then move the length cap only. Leave trim, case, and spacing in the old function.
A wider patch can change a row while looking cleaner. This file is only a proposed lab fixture. Replace any row that disagrees with your real function.
The problem this gate targets
Search boxes often grow a single normalize function. That function trims ends, folds case, collapses gaps, and cuts length. Those rules share one body, so a cleanup touches all of them.
One accidental change can alter stored query keys. A model suggestion increases the speed of that cleanup. It does not reduce the need for a pin.
Speed without a row table is how silent edits land. The table is the review tool, not the model transcript.
Step 1: Choose a narrow contract
Pick twelve inputs that the current function already handles. Include empty text, blank space, a tab, and mixed case. Include one string that is longer than the cap.
Keep secrets and customer text out of the file. Twelve rows are a gate, not a full corpus. They are enough to block a wide first extract.
They are not enough to certify international search behavior. Add further rows later in a separate commit. Row purpose stays explicit in the review notes.
Eleven rows guard rules that must not move yet. One row guards the cap you plan to move. Repeating mixed rows makes a drive-by edit fail.
Step 2: Write the pin before the move
Save the messy function beside the pin test. The test builds an actual list and a pinned list. It compares both lists in a single assertion.
A mismatch fails the gate before any style debate. Run the file before you request any rewrite. A failing pin stops the extract before any rewrite.
The commands below are a reproducible plan, not a reported run. Treat a local green result as the only passing record.
import unittest
def normalize_query(raw):
text = "" if raw is None else str(raw)
text = text.replace("\t", " ")
text = text.strip().lower()
text = " ".join(text.split())
if len(text) > 32:
text = text[:32]
return text
CASES = [
(" Hello ", "hello"),
("WORLD", "world"),
("", ""),
(" ", ""),
("a", "a"),
("Café", "café"),
("foo bar", "foo bar"),
("x" * 40, "x" * 32),
(" MIXED Case ", "mixed case"),
("tab\there", "tab here"),
("null", "null"),
(None, ""),
]
class QueryPinTest(unittest.TestCase):
def test_twelve_rows_stay_put(self):
actual = [normalize_query(raw) for raw, _ in CASES]
pinned = [out for _, out in CASES]
self.assertEqual(actual, pinned)
if __name__ == "__main__":
unittest.main()
python -m unittest query_pin.py -v
A green run means the table matches this function. A red run means the table is wrong for this code. Edit the table only to restore the current contract.
Do not repair the function during the pin step. The pin must describe behavior you already ship. A repair belongs in a later, separate commit.
Step 3: Map each row to a rule
Read the rows as data, not as sample flavor. The mapping below is the working review checklist. Use it when a patch claims to be behavior neutral.
| id | input shape | pinned output | rule that must hold |
|---|---|---|---|
| 1 | padded Hello | hello | trim, then lower |
| 2 | WORLD | world | lower without trim |
| 3 | empty string | empty string | empty stays empty |
| 4 | three spaces | empty string | blank becomes empty |
| 5 | single a | a | short text stays |
| 6 | Café | café | simple Unicode lower |
| 7 | foo and spaced bar | foo bar | inner gaps collapse |
| 8 | forty x letters | thirty-two x letters | hard length cap |
| 9 | padded MIXED Case | mixed case | trim, collapse, lower |
| 10 | tab inside words | tab here | tab becomes a space |
| 11 | text null | null | literal word stays |
| 12 | None | empty string | missing input is empty |
Only row 8 justifies this first helper move. If a diff changes row 6 or row 10, reject it. Style cleanup is not a reason to retune those outputs.
Step 4: Use a free bench without widening scope
You can draft the helper with free model access. You can run the same file on a free server option. Disclosure: This article was prepared as part of MonkeyCode's product outreach.
Those two options are a drafting bench for this exercise. This article does not name a model or a quota. It does not claim hardware, uptime, or a permanent offer.
Confirm the current plan before you depend on it. Send a narrow prompt with the fixture attached. Ask for a length-cap helper and one call site.
Forbid edits to strip, lower, split, and the test rows. Require the patch to keep the public function name. Stop the session if the reply widens the ask.
Step 5: Score the returned diff
Do not apply a suggestion because it reads well. Score the patch against the scope table first. Then run the twelve-row pin on that result.
A green test cannot rescue a diff that rewrote the wrong rule. Reject the patch before you debate naming style. Scope failure is enough to stop the merge.
| diff signal | decision | why |
|---|---|---|
| Helper plus one call | run the pin | scope matches the ask |
| Changes to strip or lower | reject | those rules stay put |
| Test rows rewritten | reject | the pin would be fake |
| New import or package | reject | this slice needs neither |
| Missing fixture on the server | reject | no contract to compare |
| Cap limit changed | reject | the pin uses 32 |
Count the touched symbols if the patch is noisy. Acceptable symbols are the helper name and the cap call. Any other symbol edit returns the patch to the bench.
Do not negotiate scope inside the same commit. Open a new review if the helper needs a second argument. Keep the first commit limited to the cap call.
Step 6: Apply the smallest safe change
After a scoped diff passes review, apply only that move. Keep the earlier lines in their current order. Pass the already cleaned text into the helper.
Leave the limit at 32 so row 8 stays stable. The helper should slice text and do nothing else. Hidden trim inside that helper fails this gate.
def cap_query(text, limit=32):
return text[:limit]
def normalize_query(raw):
text = "" if raw is None else str(raw)
text = text.replace("\t", " ")
text = text.strip().lower()
text = " ".join(text.split())
return cap_query(text)
Run the pin again with the same command. Green means the twelve outputs survived the move. Red means the helper changed a boundary or a type.
Revert the commit and inspect the call before a second try. Compare the helper body with the slice shown above. Accept only a plain slice of already cleaned text.
Step 7: Read the patch as a rule list
Review by output rule, not by line aesthetics. Empty input must still return an empty string. A tab must still collapse into a single space.
Forty x characters must still become thirty-two letters. Check the helper signature against the call site. The default limit must remain 32 in this gate.
A keyword argument that overrides it in the caller fails the pin. If the pin is green, stop and commit this slice alone. Do not extract spacing in the same commit.
The next candidate is the tab replace, still under this pin. That later move needs its own review note and its own diff. Batching both moves removes the point of the gate.
Failure cases worth keeping
One common failure is a helper that also strips. The model sees messy text and finishes the cleanup. Row 2 can still pass if the extra strip is harmless.
Row 10 can fail if tab handling moves with the strip. Another common failure is a rewritten test assertion. The patch changes expected text to match the new code.
The test goes green and the contract is gone. Reject that pattern even when the prose sounds careful. A third failure is a cap of 24 or 64.
The helper looks equivalent because both versions slice text. Row 8 exposes that changed number at once. Keep that row in the file so the limit cannot drift quietly.
A fourth failure is sample data from a real log. Pasted queries can contain emails, tokens, or names. A free server run does not make that paste safe.
Use synthetic rows like the ones in this fixture. Label the file as an exercise if you share it. Do not paste production queries into a remote session.
Limits of the green table
A green pin proves a match, not correctness. If the product cap should change, this test blocks it. Make that change in a separate commit with an edited row.
State the product reason in the commit message. Python lower is not a full case fold for every locale. Row 6 covers one character, not every script.
Combining marks and Turkish casing can still diverge. Extend the table before you treat this function as global search. The free server option is only a place to execute the file.
It does not rank suggestions or certify safety. A local run remains the deciding result for the merge. If remote and local runs differ, trust the local pin.
Investigate the gap before you merge either result. This method also locks bugs you have not named yet. That is acceptable for a move, and wrong for a repair.
Keep pure move commits apart from behavior-change commits. Reviewers can then see which commit altered a row. That split is the whole point of the gate.
Who should skip this approach
Skip it if you cannot execute the unittest locally. Skip it if today's output is too wrong to freeze. Skip it when the sample set would contain private data.
Skip it for payments, authentication, or migration code as a first drill. Skip it if the goal is a new query language. This brake preserves behavior while one helper moves.
It will fight you if the contract itself must change. Use a design note and a new table for that work. Skip it if nobody will read the diff scope table.
A model bench without a human reject rule is just faster editing. The table is the control, and the test is the alarm. Remove either one and the workflow no longer holds.
One check to run next
Attach this fixture to a free model session. Request only the length-cap helper in that session. If the returned patch touches any other rule, discard it.
Rerun the twelve-row pin before you open a second extract. Keep the same twelve rows as the only acceptance check.
Top comments (0)