feat(systems): bootstrap mints directly — the standard vocabulary replaces operator approval (#2798)
CI & Build / Python lint (push) Successful in 5s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / integration (push) Successful in 40s
CI & Build / Python tests (push) Successful in 1m5s
CI & Build / Build & push image (push) Successful in 59s
CI & Build / Python lint (push) Successful in 5s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / integration (push) Successful in 40s
CI & Build / Python tests (push) Successful in 1m5s
CI & Build / Build & push image (push) Successful in 59s
Operator directive: creating Systems is the agent's call, not an approval flow — "propose to the operator, create each confirmed one" made the operator a permission gate. The bootstrap ask now says create_system 3-6 directly, in-session, and the consistency that approval was covering moves to a standard cross-project vocabulary (CI & Release, Auth & Access, Data Model & Storage, API Surface, UI & Design, Import & Export, Background Jobs, Observability): use the standard name verbatim where the area fits, mint freely beyond it, the duplicate gate guards sprawl. create_system and enter_project docstrings carry the same contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -78,10 +78,10 @@ async def enter_project(project_id: int) -> dict:
|
||||
a subsystem's accumulated records with list_system_records.
|
||||
|
||||
`systems_bootstrap` appears ONLY when the project has many records and no
|
||||
Systems at all — act on it before starting other work: propose a starter
|
||||
vocabulary from the areas the project's records name, confirm it with the
|
||||
operator, and create_system the confirmed set. It stops appearing the
|
||||
moment the first System exists.
|
||||
Systems at all — act on it before starting other work: create_system a
|
||||
starter vocabulary from the areas the project's records name, directly
|
||||
and without asking permission, preferring the standard names the ask
|
||||
lists. It stops appearing the moment the first System exists.
|
||||
|
||||
`design_system` is null unless the project points at one. When present it
|
||||
carries the chain-merged guidance (the house style AND this project's
|
||||
@@ -129,7 +129,7 @@ async def enter_project(project_id: int) -> dict:
|
||||
|
||||
# The arrival-moment half of the bootstrap ask (#2683): session start is
|
||||
# when the agent has just read the project map and is not yet deep in a
|
||||
# task — the one moment "propose a starter vocabulary" is cheap. The
|
||||
# task — the one moment minting a starter vocabulary is cheap. The
|
||||
# write-moment half rides untagged-record responses (attach_systems);
|
||||
# both retire the instant the first System exists.
|
||||
systems_bootstrap = None
|
||||
|
||||
@@ -23,6 +23,18 @@ from scribe.services import systems as systems_svc
|
||||
_BOOTSTRAP_MIN_RECORDS = 20
|
||||
_BOOTSTRAP_TITLES = 6
|
||||
|
||||
# The standard vocabulary (#2798): area names that recur across software
|
||||
# projects, offered so "CI & Release" means the same thing in every project
|
||||
# on the instance. Consistency comes from the shared names — NOT from asking
|
||||
# the operator to approve each System; agents mint directly. Generic by
|
||||
# design (rule #115): archetypes any codebase could have, never one
|
||||
# install's subsystems. Mint freely beyond the list; the duplicate gate
|
||||
# guards sprawl.
|
||||
_STANDARD_SYSTEMS = (
|
||||
"CI & Release", "Auth & Access", "Data Model & Storage", "API Surface",
|
||||
"UI & Design", "Import & Export", "Background Jobs", "Observability",
|
||||
)
|
||||
|
||||
|
||||
async def bootstrap_systems_ask(user_id: int, project_id: int) -> str | None:
|
||||
"""The escalated vocabulary-bootstrap ask for a mature zero-Systems project.
|
||||
@@ -33,8 +45,10 @@ async def bootstrap_systems_ask(user_id: int, project_id: int) -> str | None:
|
||||
(#2683). What separates the nudges that convert from the prose that
|
||||
doesn't (the duplicate gate, the prior-art "already defined in 2 files")
|
||||
is the project's OWN evidence in the ask — so this one carries the record
|
||||
count and the recent titles, and asks for a concrete deliverable: propose
|
||||
a starter set, confirm, create.
|
||||
count and the recent titles, and asks for a concrete deliverable: create
|
||||
a starter set directly, preferring the standard cross-project names.
|
||||
Deliberately NOT an approval flow (#2798): the operator is not a
|
||||
permission gate for vocabulary; the standard names carry consistency.
|
||||
|
||||
Self-retiring by construction: callers only reach for it while the
|
||||
project has zero Systems, so the first create_system ends it everywhere.
|
||||
@@ -56,12 +70,15 @@ async def bootstrap_systems_ask(user_id: int, project_id: int) -> str | None:
|
||||
return (
|
||||
f"This project has {total} records and NO Systems modelled — none of "
|
||||
"them can be tagged to an area, so recurring problem-spots stay "
|
||||
"invisible. Bootstrap the vocabulary now, in this session: from the "
|
||||
f"areas the records themselves name (recent: {titles}), propose 3-6 "
|
||||
"Systems to the operator, create_system each confirmed one with a "
|
||||
"one-paragraph charter, then tag this record (system_ids=[...]). "
|
||||
"This ask repeats until the first System exists; answering it once "
|
||||
"retires it for every future record."
|
||||
"invisible. Bootstrap the vocabulary now, in this session, without "
|
||||
"asking permission — creating Systems is your call, not an approval "
|
||||
f"flow. From the areas the records themselves name (recent: {titles}), "
|
||||
"create_system 3-6 Systems, each with a one-paragraph charter, then "
|
||||
"tag this record (system_ids=[...]). Where an area fits a standard "
|
||||
f"name, use it verbatim so it means the same thing in every project: "
|
||||
f"{', '.join(_STANDARD_SYSTEMS)}. Mint freely beyond that list — the "
|
||||
"duplicate gate guards sprawl. This ask repeats until the first "
|
||||
"System exists; answering it once retires it for every future record."
|
||||
)
|
||||
|
||||
|
||||
@@ -148,11 +165,16 @@ async def create_system(
|
||||
|
||||
Create one the moment two records would share an area that has no System
|
||||
yet — the same two-or-more test snippets use. Don't wait to be asked to
|
||||
name an area that plainly exists in the code; an unmodelled area means
|
||||
every record about it stays untaggable. An audit or sweep that walks the
|
||||
codebase is a DISCOVERY moment: mint the Systems it names as it names
|
||||
them — the duplicate gate below, plus reviewing the existing list, is what
|
||||
guards against sprawl, not holding back. Give each one a one-paragraph
|
||||
name an area that plainly exists in the code, and don't route the
|
||||
creation through operator approval — minting vocabulary is the agent's
|
||||
call (#2798); an unmodelled area means every record about it stays
|
||||
untaggable. An audit or sweep that walks the codebase is a DISCOVERY
|
||||
moment: mint the Systems it names as it names them — the duplicate gate
|
||||
below, plus reviewing the existing list, is what guards against sprawl,
|
||||
not holding back. Prefer the standard cross-project names where the area
|
||||
fits one (CI & Release, Auth & Access, Data Model & Storage, API Surface,
|
||||
UI & Design, Import & Export, Background Jobs, Observability) so the same
|
||||
word means the same thing in every project. Give each one a one-paragraph
|
||||
charter, not just a label: the description is what tells a later session
|
||||
whether a record belongs here.
|
||||
|
||||
|
||||
@@ -285,7 +285,7 @@ def _enter_project_stubs(p):
|
||||
async def test_enter_project_carries_the_bootstrap_ask_when_it_fires():
|
||||
"""The arrival-moment half of #2683: a mature zero-Systems project greets
|
||||
the session with the concrete bootstrap ask, before it is deep in a task —
|
||||
the moment "propose a starter vocabulary" is cheapest."""
|
||||
the moment minting a starter vocabulary is cheapest."""
|
||||
import contextlib
|
||||
|
||||
ask = "This project has 282 records and NO Systems modelled — ..."
|
||||
|
||||
@@ -129,7 +129,11 @@ async def test_untagged_hint_escalates_in_a_mature_zero_systems_project():
|
||||
assert "282 records" in hint
|
||||
assert "Fix scrape retry backoff" in hint # the project's own evidence
|
||||
assert "3-6" in hint and "create_system" in hint
|
||||
assert "propose" in hint
|
||||
# NOT an approval flow (#2798): the agent mints directly, and the
|
||||
# standard cross-project names carry the consistency instead.
|
||||
assert "without asking permission" in hint
|
||||
assert "propose" not in hint and "confirmed" not in hint
|
||||
assert "CI & Release" in hint and "Auth & Access" in hint
|
||||
# The generic wording is REPLACED, not appended — two questions is noise.
|
||||
assert "no Systems yet" not in hint
|
||||
|
||||
|
||||
Reference in New Issue
Block a user