fix(systems): sweeps are the discovery moment — zero-Systems hint, create_system dedup gate, prose inverted
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 7s
CI & Build / TypeScript typecheck (push) Successful in 11s
CI & Build / integration (push) Successful in 16s
CI & Build / Python tests (push) Failing after 29s
CI & Build / Build & push image (push) Skipped
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 7s
CI & Build / TypeScript typecheck (push) Successful in 11s
CI & Build / integration (push) Successful in 16s
CI & Build / Python tests (push) Failing after 29s
CI & Build / Build & push image (push) Skipped
First real-world test of the #2562 fixes (Scribe issue #2569): a Forge session ran a whole-codebase audit and created zero Systems — endorsed by the shipped guidance, whose "no particular area takes none" clause read as an exemption for exactly the record type that enumerates the subsystem vocabulary. And the systems_hint was silent for a zero-Systems project, the one state nothing else nudges out of. - systems_hint gains a zero-Systems branch: prompt the FIRST create_system instead of going quiet. - create_system is duplicate-gated like the other creates (normalized name, archived included, fail-open) — liberal creation becomes safe by construction, so the guidance can stop preaching restraint. - Prose inverted on every surface (hint text, create_system docstring, floor bullet, using-scribe step 7): audits/sweeps take several tags and mint the Systems they name; the gate is the guardrail against sprawl, not holding back; only a record genuinely about no particular area goes untagged. - Plugin 0.1.26 -> 0.1.27. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -30,13 +30,24 @@ async def untagged_systems_hint(user_id: int, project_id: int) -> str | None:
|
||||
except Exception:
|
||||
return None
|
||||
if not systems:
|
||||
return None
|
||||
# Zero Systems is the one state nothing else nudges out of: the hint
|
||||
# below needs a vocabulary to name, so without this branch the FIRST
|
||||
# create_system depends entirely on prose that demonstrably doesn't
|
||||
# fire at write time (#2562).
|
||||
return (
|
||||
"Created untagged — this project has no Systems yet. If this "
|
||||
"record is about a code subsystem/area, create_system it (name + "
|
||||
"a one-paragraph charter) and tag the record. An audit or sweep "
|
||||
"that names areas is exactly the moment to mint them; the "
|
||||
"duplicate gate is what guards against sprawl, not holding back."
|
||||
)
|
||||
names = ", ".join(f"#{s.id} {s.name}" for s in systems)
|
||||
return (
|
||||
f"Created untagged. This project's Systems: {names}. If this record is "
|
||||
"about one of those areas, tag it (update it with system_ids=[...]); if "
|
||||
"its area is missing, create_system it and tag; if it is about no "
|
||||
"particular area, leave it untagged."
|
||||
"about one or more of those areas, tag it (update it with "
|
||||
"system_ids=[...]) — cross-cutting records like audits take several; "
|
||||
"if an area it names is missing, create_system it and tag; only leave "
|
||||
"it untagged if it is about no particular area."
|
||||
)
|
||||
|
||||
|
||||
@@ -54,17 +65,46 @@ 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. Give it a one-paragraph charter,
|
||||
not just a label: the description is what tells a later session whether a
|
||||
record belongs here.
|
||||
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
|
||||
charter, not just a label: the description is what tells a later session
|
||||
whether a record belongs here.
|
||||
|
||||
Args:
|
||||
project_id: The project this system belongs to (required).
|
||||
name: Short label (required).
|
||||
description: What the system is and how it's used — a name is rarely enough.
|
||||
color: Optional UI accent (hex), or empty.
|
||||
|
||||
Duplicate-gated like the other creates: if a System with the same
|
||||
normalized name already exists in this project (archived included), the
|
||||
call returns {"duplicate": true, "existing_id": ...} instead of creating —
|
||||
tag records to that one, or update_system it if its charter needs work.
|
||||
"""
|
||||
uid = current_user_id()
|
||||
norm = " ".join(name.split()).lower()
|
||||
if norm:
|
||||
try:
|
||||
existing = await systems_svc.list_systems(
|
||||
uid, project_id, include_archived=True
|
||||
)
|
||||
except Exception:
|
||||
existing = []
|
||||
for s in existing:
|
||||
if " ".join(s.name.split()).lower() == norm:
|
||||
return {
|
||||
"duplicate": True,
|
||||
"existing_id": s.id,
|
||||
"message": (
|
||||
f"System '{s.name}' (#{s.id}) already covers this area "
|
||||
"in this project. Tag records to it with system_ids, "
|
||||
"or update_system it if the charter needs revising — "
|
||||
"a second System with the same name would split the "
|
||||
"area's records across two piles."
|
||||
),
|
||||
}
|
||||
system = await systems_svc.create_system(
|
||||
uid, project_id=project_id, name=name,
|
||||
description=description or None, color=color or None,
|
||||
|
||||
Reference in New Issue
Block a user