fix: uv sync --locked validates against pyproject.toml #1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/dockerfile-uv-locked"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Three changes. Each one closes a place where a surface stated something the code does not enforce.
1. The brief told every role
ae's write boundaryBriefService.constraints_blocktookstateand ignored it —noqa: ARG004— so every dispatch, whatever its role, was told:That is
ae's boundary. Anatedispatch was therefore told not to write the tests that are its entire output, and aci_fixdispatch was told the gate would refuse the workflow it was sent to repair. A false constraint is worse than a missing one: the agent either stops short of the task, or discovers one of the rules was invented and has less reason to believe the rest._ROLE_SCOPEinapp/services/briefs.pynow mirrorsROLE_POLICIESindogsled/src/dogsled/job/policy.py. Each role carries probe paths as well as prose, andtests/test_role_scope_parity.pyruns them through dogsled's ownpolicy_for(role).permits()— prose cannot be checked, probes can. Both directions are tested: a path the brief offers that the gate refuses (expensive — the run is discarded), and a path the brief withholds that the gate permits (quiet — the agent just does less than the task asked, which is theatecase).The block also gained a universal-deny line, derived from dogsled's
UNIVERSAL_DENY:.git/,.env,*.pem,*.key,secrets*are refused for every role and the brief never said so.Also fixed:
_REPAIR_TEMPLATE. It is theci_fixpath and carried the identical false claim. Fixing only_CONSTRAINTSwould have left the brief wrong for the very role named in the report.render_repairresolves with aci_fixdefault rather than inheritingroles.dogsled_role'sae.Wired into the
Upstream ParityCI job beside_mirror.pyanddogsled_contract.py. Unlike those two, this restatement has no runtime signal at all — a brief describing the wrong boundary is valid input to dogsled and the run simply does the wrong thing. The parity test is the entire signal.2.
layerwas free-form whilekindwas constrainedapp/graphs/investigate.pyconstrainedkindto five values with anunclassifiedfallback and leftlayerasstr. That is the same field the bug capture template already ships as aSuspected layertext box, and production returns it 88%logic— the same degeneration astypeat 100%bug, for the same reason: nobody chooses.Seven layers, each landing on a different target:
route,contract,client,session,logic,store,ui.routeandclientboth surface as a 404 and are fixed in opposite repositories;storeandlogicboth surface as a wrong answer from a handler that ran. A label that cannot separate those is not worth filtering on.SYSTEMthe same wayKINDSis — a closed field the model is not shown is a field it cannot hit;normalise_findingconstrains it identically, case-folded, with alayer_meansgloss besidekind_means;app/services/proposals.pywrote"layer": ""; now"unclassified", with its own gloss. It makes no model call, so there is no diagnosis to classify — that is a fact about the path, not missing data.The test fixture's
layerwas prose ("backend/app/routers — no notifications module is registered"). It is now"route", and the old value is reused as the invented-layer case — the degeneration documented as a test rather than a comment.3.
eng_handle_cluster— one tool, routed mechanicallyThe operator had to choose between
eng_reproduce_clusterandeng_investigate_cluster. The two differ only in whether the cluster's capture already contains the failing request, which isreproduction.from_reports— a function, not a judgement — and the signature does not show the answer. Measured onflexiscm2026-07-29: 41 of 78 open reports carry a capture, 37 do not. Guessing wrong costs two model calls and 37k tokens of source listing to be told what the capture already said, or a 400.reproduction.route— pure, a thin read overfrom_reports, deliberately not a second notion of "enough evidence". A router with its own rule would drift and sendreproducea cluster it then refuses. One test pins the agreement directly.ProposalView.route_cluster— prechecks withneeds_model=False, so an instance with no LLM can still route, including to the branch that needs one.require_investigatoris where that branch pays.POST /api/v1/items/{id}/handle→eng_handle_cluster. 200 with the parked proposal on the fast path, 202 with an acknowledgement on the investigation, andpathin the body either way so a caller never infers it from the status.forge.calls == 1is asserted, and so is the fact that a directreproducecaller still prechecks for itself.Both specific tools stay as explicit overrides — forcing a diagnosis on an evidenced cluster is a legitimate thing to want — but neither is the default.
Spec Drift Callouts
ci_fixhas no per-role deny-list. The task brief specified "ci_fix: … Cannot touchapp/,src/,tests/". dogsled's actual policy isRolePolicy(role="ci_fix")— noallow, nodeny, onlyUNIVERSAL_DENY. Since the brief also said the constraints must agree with what dogsled enforces rather than restate it, I followed the code:ci_fix's constraint states its limit as judgement ("change what the failing check requires and nothing else; deleting the failing test is not a repair") rather than as a gate that does not exist.test_ci_fix_carries_no_per_role_denialpins both sides, so a future narrowing in dogsled forces the text to change with it._REPAIR_TEMPLATEwas in scope by implication, not by name. See above — fixed, because it is theci_fixbrief.compose_brief, not before.check_rolelives inenqueue_job, so an unsupported agent type reaches brief composition with a brief already rendered.scope_fortherefore falls back rather than raising; raising would turn an operator park into a crash. Documented on the method.conftest.pyin this repo. The role prose still names it, because it is dogsled's pattern and every repo an agent is dispatched into may have one.mypyreports 60 pre-existing errors across 24 files. It is not a CI gate here (the Lint step runsruffonly) and this branch adds none.Verification
The 3 skips are
test_dogsled_client.py, which needs live dogsled credentials. Run againsthaskos_mirror_v1locally so the smeos-table tests execute rather than skip — onhaskos_eng_testthe same suite reports 587 passed / 187 skipped, which is the gap CI closes by cloning smeos and running its migrations.New parity coverage runs in
Upstream Parity(informational,continue-on-error), which already clones dogsled atdev;test_role_scope_parity.pyderivespolicy.pyfrom the existingDOGSLED_SRCvariable, so no new CI secret or variable.🤖 Generated with Claude Code
https://claude.ai/code/session_01XFrACc19eGQYck4jd2NJWc