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.
|
a subsystem's accumulated records with list_system_records.
|
||||||
|
|
||||||
`systems_bootstrap` appears ONLY when the project has many records and no
|
`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
|
Systems at all — act on it before starting other work: create_system a
|
||||||
vocabulary from the areas the project's records name, confirm it with the
|
starter vocabulary from the areas the project's records name, directly
|
||||||
operator, and create_system the confirmed set. It stops appearing the
|
and without asking permission, preferring the standard names the ask
|
||||||
moment the first System exists.
|
lists. It stops appearing the moment the first System exists.
|
||||||
|
|
||||||
`design_system` is null unless the project points at one. When present it
|
`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
|
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
|
# 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
|
# 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);
|
# write-moment half rides untagged-record responses (attach_systems);
|
||||||
# both retire the instant the first System exists.
|
# both retire the instant the first System exists.
|
||||||
systems_bootstrap = None
|
systems_bootstrap = None
|
||||||
|
|||||||
@@ -23,6 +23,18 @@ from scribe.services import systems as systems_svc
|
|||||||
_BOOTSTRAP_MIN_RECORDS = 20
|
_BOOTSTRAP_MIN_RECORDS = 20
|
||||||
_BOOTSTRAP_TITLES = 6
|
_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:
|
async def bootstrap_systems_ask(user_id: int, project_id: int) -> str | None:
|
||||||
"""The escalated vocabulary-bootstrap ask for a mature zero-Systems project.
|
"""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
|
(#2683). What separates the nudges that convert from the prose that
|
||||||
doesn't (the duplicate gate, the prior-art "already defined in 2 files")
|
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
|
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
|
count and the recent titles, and asks for a concrete deliverable: create
|
||||||
a starter set, confirm, 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
|
Self-retiring by construction: callers only reach for it while the
|
||||||
project has zero Systems, so the first create_system ends it everywhere.
|
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 (
|
return (
|
||||||
f"This project has {total} records and NO Systems modelled — none of "
|
f"This project has {total} records and NO Systems modelled — none of "
|
||||||
"them can be tagged to an area, so recurring problem-spots stay "
|
"them can be tagged to an area, so recurring problem-spots stay "
|
||||||
"invisible. Bootstrap the vocabulary now, in this session: from the "
|
"invisible. Bootstrap the vocabulary now, in this session, without "
|
||||||
f"areas the records themselves name (recent: {titles}), propose 3-6 "
|
"asking permission — creating Systems is your call, not an approval "
|
||||||
"Systems to the operator, create_system each confirmed one with a "
|
f"flow. From the areas the records themselves name (recent: {titles}), "
|
||||||
"one-paragraph charter, then tag this record (system_ids=[...]). "
|
"create_system 3-6 Systems, each with a one-paragraph charter, then "
|
||||||
"This ask repeats until the first System exists; answering it once "
|
"tag this record (system_ids=[...]). Where an area fits a standard "
|
||||||
"retires it for every future record."
|
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
|
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
|
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
|
name an area that plainly exists in the code, and don't route the
|
||||||
every record about it stays untaggable. An audit or sweep that walks the
|
creation through operator approval — minting vocabulary is the agent's
|
||||||
codebase is a DISCOVERY moment: mint the Systems it names as it names
|
call (#2798); an unmodelled area means every record about it stays
|
||||||
them — the duplicate gate below, plus reviewing the existing list, is what
|
untaggable. An audit or sweep that walks the codebase is a DISCOVERY
|
||||||
guards against sprawl, not holding back. Give each one a one-paragraph
|
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
|
charter, not just a label: the description is what tells a later session
|
||||||
whether a record belongs here.
|
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():
|
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 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 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
|
import contextlib
|
||||||
|
|
||||||
ask = "This project has 282 records and NO Systems modelled — ..."
|
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 "282 records" in hint
|
||||||
assert "Fix scrape retry backoff" in hint # the project's own evidence
|
assert "Fix scrape retry backoff" in hint # the project's own evidence
|
||||||
assert "3-6" in hint and "create_system" in hint
|
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.
|
# The generic wording is REPLACED, not appended — two questions is noise.
|
||||||
assert "no Systems yet" not in hint
|
assert "no Systems yet" not in hint
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user