Repository navigation
factory: decide first, with checkpoints a person can change direction from - #223
Open
LittleChimera wants to merge 5 commits into
Open
LittleChimera wants to merge 5 commits into
LittleChimera wants to merge 5 commits into
Conversation
|
|
||
|
|
||
| def _error(status: int, code: str, message: str) -> JSONResponse: | ||
| return JSONResponse(status_code=status, content={"error": {"code": code, "message": message}}) |
… from
Every decision point goes to a server policy before a person sees it: an
agent's question (sys_factory_ask), a decision it takes itself (the new
sys_factory_decide) and a projects proposal that would wait for an
operator. The factory decides what is reversible, within budget, not Tier 0,
has a recommended option and is not reserved by the owners' per-class,
per-subtree settings (autonomy.decide) or the autonomy level's defaults;
the rest waits for a person as before.
Every decision is a checkpoint (factory_checkpoints, K-n); a person's
answers (D-n) and applied proposals (C-n) read as checkpoints through an
adapter. Node pages count what the factory decided since the viewer's last
look, GET .../n/{ref}/decisions lists them, and a person changes direction
from any checkpoint with preview, apply and Undo: what can be taken back is
(Undo, cancels, charters, revert tasks for merged code), and the deciding
agent is told to re-plan. Open asks from before are decided by the sweep at
the levels that allow it; merges into main and asks without a
recommendation still wait. The Slack digest says "Decided since you looked".
The lead and PL bundles teach decide first (text only).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…irst line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ads it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- An ask that waits keeps what it was routed with (a hidden `asked` checkpoint: declared class, reversible, for=, subject, words, holds, definition version); the sweep re-routes only level- or owner-held asks, with those inputs, once per definition version. Tier 0, irreversible and for= never auto-decide later. - A deduped twin is routed with its own inputs, never the new request's; another question under the same key is never decided; decide_ask refuses an option the ask lacks. - Tier 0 is broad and generic (prod flags, merges, credentials, spend, infra, data deletion, personal data, plus the bases people merge into); "other" is in no level's defaults, so an unclassified point asks a person. - A repeated question gets the factory's decided choice or the person's change of direction in force, never a stale choice. - A decided follow-up counts only when the server decided it on an ask, once, for a project beside the asker's. - Owners' subtree settings read the subject or node a point is about. - Change direction follows answer authority, applies once under the verb lock (409 already_changed), restores the right charter, plans reverts for merged code of finished projects and lists PRs merged since. - sys_factory_decide records nothing while nothing is the factory's and opens non-blocking asks otherwise; bundle text narrowed. - Owner reservations hold level-applied proposals; a person's K-n carries approval like a D-n; the digest's decided line is quiet after 24 h and respects visibility; the sweep reads settings once and skips opportunities. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LittleChimera
force-pushed
the
factory/decide-first-server
branch
from
November 4, 2026 17:41
96c965d to
be33f40
Compare
5 of 13 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issue
Closes # (decide first and checkpoints:
notes/factory-v3/decide-first.md§1–§4 and §6, server and agent side; the web side is a separate PR)Summary
Luka, 2026-11-04: "factory should always see if it can decide itself, and record decisions somewhere. i as a user, should be able to see decisions that it made and change direction if needed. each decision point is like a checkpoint" and "right now, there's no reason it shouldn't continue on its own to build the assistant".
ELI5: before, every agent question and most project proposals waited for a person. Now each one first goes to a policy. If the factory may decide it (reversible, within budget, not Tier 0, has a recommended option, not reserved by an owner or the autonomy level), it decides at once, writes a checkpoint and the agent keeps going. A person sees "Decided since you looked · n" on every node, opens the list, and can change direction from any checkpoint: the factory undoes what it can and the agent re-plans.
Spec → what this PR does
§1 Decide first + §2 the policy (
server/factory/decide_policy.py,decide_first.py)sys_factory_ask(POST /agent/decisions), the newsys_factory_decide(POST /agent/checkpoints), and every projects proposal that would wait for an operator (proposals.propose).for=), and no class it falls in is reserved.classand from the words (generic English; the strictest class wins, so a loose declared class cannot dodge a reserved one). A proposal's classes come from its ops only.carefulnothing (today's behaviour),standardscope / tech / UX / ordering,tryoutalso follow-ups. New work, goal or outcome changes, structural changes and anything the policy cannot classify wait at every level.autonomy.decide: [{kind, who, ref?}](class B like all ofautonomy.*: owners only,HUMAN_ORIGIN, agents get 403path_not_allowed;who: factoryis refused for Tier 0 and structural). Stored under its own top-level key (autonomy_decide) so a server from before drops only it and decides nothing.answered_by: factory, which is never a person (answer_authority.NOT_A_PERSON): a proposal carrying it out keeps its own approval rules, except that one create citing a follow-up the server decided on an ask (reply_to="D-n"or its"K-n"), beside the asker's project, is itself a follow-up.decision.answeredcard (the chat's Activity), a muted Activity line for an agent's own decision, "Decided since you looked", the digest. Proposals keep their own FYI and Undo.§3 Every decision is a checkpoint (
factory_checkpoints,K-n;checkpoints.py, store halves, migrationfza0e1f2a3b4)decided/asked/answered/changed, links (decision_id,proposal_id,overrides,changed_by).§4 Changing direction (
change_direction.py,POST …/checkpoints/{ref}/preview|apply, Undo throughPOST …/undo/{token})preview_idover the facts (409stale).changed); takes back through existing cores: a proposal in its Undo window is undone, past it the projects it created are cancelled (a person's proposal, applied at once) and charters put back; merged code gets a revert task (git revertof the merge, never a force-push); pending proposals that followed it are rejected. The same choice with only more direction re-plans without undoing.[Factory decision · K-m changed by <person> · "<option>"]with the direction quoted and what was taken back. Undo puts the old checkpoint back, re-files what was undone, cancels revert tasks, and tells the agent.§6 Existing open asks (
decide_first.sweep, in the proposals sweep every minute)askedcheckpoint). Once the settings change, an ask that only the level or an owner held is routed again with those inputs and decided with its recommended option when allowed; the asker is mailed[Factory decision · D-n answered by the factory · "<option>"]. Asks from before decide-first are routed once from their words. Pending projects proposals the policy now allows are applied. Merges, Tier 0,reversible=false,for=and asks without a recommendation never are. A careful factory with no widening setting skips the sweep entirely.§5 (server half)
decided_since(left out at 0; since the viewer's last look, else the last 7 days).GET …/n/{ref}/decisions?cursor=&asked=1: the subtree's checkpoints newest first, one line each (asks stay in Your move unlessasked=1).GET …/checkpoints/{K-n|D-n|C-n}: the details (the peek). Shapes incheckpoint_views.py, validated on every response./factory/<fid>/n/product?lens=decisions.Agent side
sys_factory_decidefor leads and PLs (runner + schema + registration);sys_factory_asktakesclass. Safe both ways: on an older serversys_factory_decideanswersnot_served(ask with your choice recommended); on an older host the protocol says to ask that way; the server decides it at once either way.K-n changed by.Deviations
decisioncard in Activity, an agent's own decision as a muted system line.Review fixes (second round)
Blockers:
askedcheckpoint with its declared class,reversible,for=, subject, the words it was routed on, the verdict withholds(tier0,irreversible,to_person,budget,no_option,never,owner,level) and the definition version. The sweep re-routes only rows whose holds are alllevel/owner, only once per definition version, with the saved inputs. Tier 0 (declared or worded),reversible=falseandfor=are never decided later. Asks from before decide-first are routed once from their words, and the route is then kept; an ask younger than 2 minutes with no kept route is left to its own request.decide_askrefuses an option that is not one of the row's options. A repeated answer is served only for the same key and question.otheris in no level's defaults, so an unclassified point goes to a person. Tier-0 words are broad and generic. prod_flag: flag … on, enable … flag/for, n% of traffic, everyone, for customers, to the public, go live, launch, release, publish, rollout. merge: land a PR/change, main/master/default/base/release branch, ship/push … to main, into main, plus the bases of active projects that are not auto-merge targets. credentials: token (not design tokens), key (not primary/cache/sort … keys), signing, certificates,.env, env file, vault. spend: buy, pay, paid, pricing, billing, invoice, cards, plans/tiers, upgrades of a plan, dollars. infra: deploy, environment, CI workflows, buckets, domain names. delete_data: remove/reset/wipe/overwrite … records/data/database/users. pii: addresses, user or full names, user data. Every phrasing from the review is a parametrized test, and the three live shapes still go to the factory at tryout.Majors:
sys_factory_decideis answered only fromdecidedrows. When a person changed direction, it gets the person's checkpoint (their choice or direction), never the stale choice. A repeated decision whose earlier choice differs is told the recorded choice.list_checkpoints(status=…)was added to both stores.K-n. It counts once: another pending or applied proposal citing it makes the next one wait. The created project must sit beside the asker's project (depends on it, or is on its repo).project:,task:,pr:,D-n,I-n/O-n/G-n), the strictest first, in asks,sys_factory_decideand the sweep.D-n, or aK-ndecided on one (or a change of direction from either), assigned to the owners or to one person can be changed only by them. The preview showscan_apply: falsewith who to ask, apply returns 403not_assigned, and the peek'scan_changematches.already_changed, one person checkpoint and one re-plan mail.beforeof the proposal being taken back, carried in the step.C-nan agent recorded as a checkpoint's effects count as followers.sys_factory_decidereturnsnot_recorded("decide as before") and opens no ask unless the choice is Tier 0 or irreversible. Elsewhere a held decision opens a non-blocking ask. The bundle text and tool description narrow the tool to choices you would otherwise ask a person about, not routine implementation choices.Minors:
who: personalso holds a proposal the level would apply (aowner_reserved:warning; requested, carried and minor-update ones are untouched).K-ncarries their approval like an answeredD-n(reply_to="K-n", same guards: in force, a person with EDIT, given to that agent, routine fields, one proposal).Test Plan
tests/server/factory/test_decide_first_guards.py(36) covers each review finding. The sweep never decides a declared Tier-0 or irreversible point; an owner-reserved declared class waits until the owner lifts it; a repeated question is routed as first asked (the merge twin,for=); no option the ask lacks is chosen; every review phrasing is Tier 0, and unclassified points wait at standard; a human-merged base is a merge; a person's change of direction answers a repeated ask or decision; a follow-up is used once and an agent cannot mint one; owner subtree settings read a lead's subject or node; change direction keeps to who may answer; a double apply changes once (stored and adapted); restore uses the rightbefore; a finished follow-up's merged PR gets a revert task; an owner reservation holds a level-applied proposal; a re-plan citing the person'sK-napplies; the digest's visibility and 24 h rule. The existing decide-first tests were updated for the hiddenaskedrows andnot_recorded.tests/server/factory(SQLite,-n 8) green;tests/examples,tests/runner/test_factory*, checkpoint store, migration and enum tests: 624 passed. PostgreSQL 16 (throwaway, port 55551): alembicupgrade head,downgrade -1,upgrade headwith a single headfza0e1f2a3b4and no model diff; store and decide-first suites withOMNIGENT_TEST_DB_URI: 91 passed. ruff clean; pyrefly 28 (pre-existing, none in touched files).tests/server/factory/test_decide_first.py(46): the live regression (follow-up, tab placement and edge-path asks with recommended options are decided at tryout, answered as the factory, recorded with class, policy and facts, no answer mail, nothing in Needs you); a merge into main still waits; Tier 0 never auto-decides (declaredclassor words; owners cannot give it to the factory); careful unchanged;for=and no recommendation wait; a repeat returns the same checkpoint; an open twin is decided when asked again; owners reserve and widen by class and subtree; agents and operators cannot widen the policy; the sweep decides old open asks and mails the asker, leaves merges and open-ended asks; pending upkeep applies once the level allows;sys_factory_deciderecords or asks; a decided follow-up applies its project; the adapter shows old D-n and C-n; node pages count decided-since and the lens lists them; change direction undoes an applied proposal, records the overriding checkpoint and re-plans; change direction from a decided follow-up cancels its project, quotes the direction, and Undo puts the checkpoint back.tests/server/factory/test_decide_first_digest.py,tests/stores/test_factory_store_checkpoints.py(SQL + memory),tests/db/test_migration_factory_checkpoints.py,tests/runner/test_factory_decide_tool.py,tests/examples/test_factory_decide_first_text.py; updated registration, definition golden and autonomy expectations.upgrade head→downgrade -1→upgrade headonfza0e1f2a3b4; models match the migrated schema (compare_metadataempty); store, decide-first and digest tests withOMNIGENT_TEST_DB_URI: 53 passed.-n 8):tests/server/factory, the factory runner tests,tests/examples, the factory store and migration tests: 5453 passed, 1 failed (test_decision_routing::test_non_blocking_decisions_and_custom_windows: an ask now reads the definition first and pins the seeded v1, so the test writes its definition before the asks; fixed and re-run with the decide-first, node, routing and bundle suites: 274 passed).FORBIDDEN_SAMPLE_TOKENSgreen.How to try it on a factory: set Steer › autonomy level to tryout, have an agent ask a question with a recommended option (or
sys_factory_decide); the tool result saysstatus: "answered"withdecided, the node shows "Decided since you looked · 1",GET /v1/factory/factories/<fid>/n/product/decisionslists it, andPOST …/checkpoints/K-1/preview {"option_id": "2"}then…/applychanges direction and mails the agent.Demo
N/A (server and agent side; the cockpit's Decisions lens is the web PR).
Type of change
Test coverage
Coverage notes
Migration verified on PostgreSQL 16 (upgrade, downgrade -1, upgrade) and the store tests ran there too.
Changelog
The factory decides what its autonomy allows, records every decision as a checkpoint, and lets a person change direction from any of them.
🤖 Generated with Claude Code