Repository navigation
fix(operator-queue): an empty options list or a numeric title no longer reads as a rewrite (#3314) - #3414
Conversation
…d as a rewrite (#3314) The #2915 sync fingerprint must reproduce what ingest stored. Two seams diverged, so an untouched agent entry read SYNC_CHANGED and every operator answer was refused 409 item_diverged: - options: the store keeps a falsy value ([], "", {}, 0, False) as NULL, but `_canonical_options` serialised it ("[]"). It now canonicalises any falsy value like None, mirroring the store's truthiness. - title / question: a JSON number or bool passed the clamp untouched and was stored verbatim (read back backend-dependently: SQLite True -> "1", PG "true"), while `_entry_content` fingerprinted it as the default. A new `_scalar_text` stores it as its text ("123", "True") in the clamp and mirrors that in `_entry_content`, so both sides match on every backend. Not covered: no number conversion before the #3243 title cap check (a numeric title is still never cap-held; scope kept narrow). `type` handling untouched (#3385). Stale rows: asks stored before this fix with a boolean title hold "1" on SQLite and stay flagged as diverged; integer-titled rows recover on the next sync. No migration. Tests: the strict xfails r88-r91 in test_ec_operator_queue_edges.py are removed and r90b (boolean title) added. With the fix absent all five go red: r88/r89 `['options'] == []`, r90/r90b `['title'] == []`, r91 `['question'] == []`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sy options and non-string titles (#3314) The Tier-2 real-SQLite property (entry -> clamp -> store -> sync read -> changed_fields == []) excluded the #3314 inputs while they were strict xfails. It now generates falsy options ([], "", {}, 0, False) and int/bool/float(nan, inf)/[]/{} titles and questions, with explicit examples for `options: []` and `title: 123`. This is the parity guard for the fingerprint mirroring the store's options truthiness. Only the null `type` exclusion (#3315/#3385) remains. With the #3314 service fix reverted the property goes red (`['title', 'question'] == []` on the explicit examples). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…3314) merge-train: mechanical, per the merge-train note on the PR. `_scalar_text(0)` returned "0", but `create_item` stores the `or`-default ("Agent request") for a falsy title/question, and every row ingested before this fix carries that default — so the entry fingerprint "0" diverged from the row and flipped such rows to 409 `item_diverged` on the next sync. Only truthy scalars are stringified now; a 0/False title keeps displaying the default as before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
merge-train: pushed bffddc0 to this branch — mechanical fix from validation. |
trinity-ability
left a comment
There was a problem hiding this comment.
merge-train: batch validated on train/20261009-0735 (#3424)
…t the database (#3385) (#3421) Fixes #3385 — one of ten small bug fixes in the trinity-pm `chain-easy-1008` run. **Stacked on #3414** (the fix for #3314): base is `feature/3314-queue-fingerprint-falsy`, because both issues edit `_clamp_ingested_item` and the fingerprint helpers. Merge #3414 first, then retarget this to `dev`. **Draft until the operator merges.** ## What - `src/backend/services/operator_queue_service.py`: new `OPERATOR_QUEUE_TYPE_MAX = 64` and `_bounded_type`. An agent-written `type` longer than 64 characters is shortened with the existing `…[truncated]` marker at ingest (`_clamp_ingested_item`); the ask is never refused or lost. `_comparable_type` applies the same bound, so a shortened row does not read as a rewrite (#2915) and a row stored before this fix at full length still compares equal. - `src/backend/db/operator_queue.py::_insert_values`: `_DB_BELT_TYPE_MAX_BYTES = 1024`; above it the insert raises `ValueError`, like the id / title / question / context belts. All three create paths go through it. - Docs: five lines in `architecture/api-endpoints.md`, `feature-flows/operating-room.md` and `requirements/security.md` that list the clamp and belt fields now name `type`. - Tests in `tests/unit/test_1632_operator_queue_caps.py`: an oversized type is shortened with the marker; every platform-emitted type and every type `ask_operator` accepts is unchanged; the belt rejects an oversized type and passes 64 four-byte characters; `test_3385_clamped_type_is_not_a_rewrite` (clamped row vs raw entry, legacy full-length row, a real rewrite inside the first 52 characters). ## Rulings carried (orchestrator, on the operator's behalf — plan file) - T1: a fixed constant, no environment variable — as recommended. - T2: 64 characters — as recommended. The longest type the platform emits is `workspace_problem_report`, 24. - T3: the DB belt raises, like its siblings — as recommended. - Orchestrator: stacked on #3314 instead of branching from `dev`, so the two fixes do not hand the operator a conflict in the same two functions. ## Review + security `/review` reading pass (claude-fable-5-1, report-only, against the stacked base): **MERGEABLE**, zero critical. Census of every literal `type` the platform and the enterprise submodule pass to the insert paths: longest is 24 characters, none is altered. Every truncated value ends in the marker, which no set member can match, so truncation cannot move a value into or out of a set that decides control flow (`_BUDGETED_ALERT_TYPES` and neighbours). The new `ValueError` is reachable only from the file sync loop, which quarantines it under its existing handler. `/cso --diff`: no findings; the error text carries the constant, never the value. Fixed after review: `eab91c10` — I1, the five doc lines. Noted, not changed: I2, a non-string `type` over 1 KiB now raises the named `ValueError` where the base failed at the bind — same quarantine outcome. Evidence sweep after the fix (claude-opus-5-5): **GREEN**. 13 operator-queue files ran shuffled (seed 12345), one file per process: 836 passed, 7 xfailed. With both production files taken from the #3314 branch, the two `TestClampType` cases, the belt test and the clamped-row fingerprint case go red. `git merge-tree` against `dev` f6bedcd is clean; the base branch has not moved (`923b1edc`). ## Tests Counts above. Not run here: `test_ent815_queue_walk`, `test_ent815_broad_list_agent_scope`, `test_ent751_gate_entries` (2–4 minutes each; they ran green on the #3314 base in that PR's sweep) and the full unit island. ## Before merge - **This PR's checks are weaker than a dev-based PR's:** a PR whose base is a feature branch runs a reduced CI set and `backend-unit-test` does not run. The first full run happens when it retargets to `dev` after #3414 merges — read that run before merging. - Accepted trade-off: an agent that rewrites an oversized type only after character 52 is not detected as a change. Titles and questions already behave this way. - Thirteen open PRs share a file with this branch (mostly `tests/registry.json` and `docs/memory/architecture/api-endpoints.md`; #3256 also `db/operator_queue.py` and the three docs). A test-merge of this head against each of #3420, #3417, #3412, #3411, #3407, #3304, #3271, #3256, #2984, #2956, #2713 and #2709 adds no conflict beyond what that PR already has with `dev`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Fixes #3314 — independent fix, one of ten small bug fixes in the trinity-pm
chain-easy-1008run. Base isdev. Draft until the operator merges. The fix for #3385 (boundingoperator_queue.type) is stacked on this branch.What
src/backend/services/operator_queue_service.py_canonical_options: any falsyoptions([],"",{},0,False,None) canonicalises to empty, the waydb/operator_queue.pystores it (NULL). An entry written withoptions: []no longer reads aschanged:options, so answers are no longer refused with 409item_diverged._scalar_text: a numeric or booleantitle/questionis turned into text in_clamp_ingested_itemand in_entry_content, so what is stored and what is fingerprinted agree on SQLite and PostgreSQL.db/operator_queue.pyis untouched.Rulings carried (orchestrator, on the operator's behalf — plan file)
"True"/"False") — as recommended.Review + security
/reviewreading pass (claude-fable-5-1, report-only): MERGEABLE, no critical findings. For every value the ingest path accepts, the stored row and the fingerprint agree on both databases; real rewrites of options, title and question are still detected; the 409item_divergedpath and the #3243 title hold are not in the diff._scalar_textstringifies only bool, int and float, and its output is still clamped./cso --diff: clean.Evidence sweep (claude-opus-5-5): GREEN. 56 test files reference the operator queue: 156 passed on the two changed files (shuffled, seed 12345) and 1,641 passed on the other 54, run one file at a time because several take 2–4 minutes each. With the fix reverted, six regression tests go red, including both tests the issue names.
git merge-treeagainstdevf6bedcd is clean.Tests
Targeted counts above. Not run here: the full unit island (CI).
Before merge
git merge-treeof the two heads is clean.[]↔""↔None, and123↔"123".Handoffs
operator_queue.typeis unbounded at ingest and at the DB sink #3385 is stacked on this branch and edits the same two functions.typeis still out of scope here (r92 stays a strict xfail; bug: operator-queue ingestion gaps — null type never ingested, lookalike guard misses format chars, id regex accepts a trailing newline #3315).🤖 Generated with Claude Code