diff --git a/src/scribe/config.py b/src/scribe/config.py index 2f2f2277..65091ec6 100644 --- a/src/scribe/config.py +++ b/src/scribe/config.py @@ -60,9 +60,9 @@ class Config: # the MCP layer doesn't proxy web search (Claude has its own). SEARXNG_URL: str = os.environ.get("SEARXNG_URL", "") - # Git forge integration (#2689) — optional read access to a git forge so + # Git forge integration — optional read access to a git forge so # snippet bodies can be fetched/verified server-side. Connections are - # per-user keyring rows (#2778, Settings → Git forges); these env values + # per-user keyring rows (Settings → Git forges); these env values # survive as an implicit keyring entry for ADMIN users' projects only, so # a deployment can keep the operator's token in a Docker secret instead # of the database. A stored row for the same host wins over the env entry. diff --git a/src/scribe/mcp/tools/repos.py b/src/scribe/mcp/tools/repos.py index a08ec3c5..fd6bc773 100644 --- a/src/scribe/mcp/tools/repos.py +++ b/src/scribe/mcp/tools/repos.py @@ -22,8 +22,8 @@ async def bind_repo(repo_url: str, project_id: int, ref: str = "") -> dict: The binding is also what the shape ledger reads (refresh_pattern_coverage): `ref` names the branch it follows. Default (""): the repo's default branch - — which means the ledger only sees work after a merge. A dev-first project - (rule 1: dev is home) should bind with ref="dev" so classification follows + — which means the ledger only sees work after a merge. A project that + works on a dev branch should bind with ref="dev" so classification follows the push, not the merge. Re-binding with ref="" keeps the standing ref; pass ref="-" to clear it back to the default branch. diff --git a/src/scribe/models/canonical_system.py b/src/scribe/models/canonical_system.py index a2f1acb7..09941d47 100644 --- a/src/scribe/models/canonical_system.py +++ b/src/scribe/models/canonical_system.py @@ -7,7 +7,7 @@ from scribe.models.base import SoftDeleteMixin, TimestampMixin, iso class CanonicalSystem(Base, TimestampMixin, SoftDeleteMixin): """A GLOBAL area name — the shared vocabulary every project's Systems - can point at (milestone 307, decision note 3026). + can point at. A `System` is per-project and NOT NULL on project_id, so nothing outside a project can reference one: a rule that applies across projects has no way diff --git a/src/scribe/models/design_system.py b/src/scribe/models/design_system.py index 0b5c29f4..10aa7315 100644 --- a/src/scribe/models/design_system.py +++ b/src/scribe/models/design_system.py @@ -135,7 +135,7 @@ class DesignToken(Base, TimestampMixin, SoftDeleteMixin): # and the token's own value are usually different colours entirely. No # value-matching rule could ever connect them. # - # Consumed by the source lint (#2277), not by the drift panel: these + # Consumed by the source lint, not by the drift panel: these # literals live in component CSS, which the panel cannot see and says so. supersedes: Mapped[list] = mapped_column( JSONB, nullable=False, default=list, server_default=text("'[]'::jsonb") diff --git a/src/scribe/models/forge_connection.py b/src/scribe/models/forge_connection.py index d922456b..262e16d8 100644 --- a/src/scribe/models/forge_connection.py +++ b/src/scribe/models/forge_connection.py @@ -6,7 +6,7 @@ from scribe.models.base import TimestampMixin, iso class ForgeConnection(Base, TimestampMixin): - """One user's read-only credential for one git forge host (#2778). + """One user's read-only credential for one git forge host. The keyring model: a user owns a set of connections and every server-side forge read for a project runs on the PROJECT OWNER's set, resolved by the diff --git a/src/scribe/models/moment_mapping.py b/src/scribe/models/moment_mapping.py index 2e1e5abf..c6f9d486 100644 --- a/src/scribe/models/moment_mapping.py +++ b/src/scribe/models/moment_mapping.py @@ -6,7 +6,7 @@ from scribe.models.base import CreatedAtMixin, iso class MomentMapping(Base, CreatedAtMixin): - """One install's correction to which actions reach which moment (milestone 458). + """One install's correction to which actions reach which moment. The moments are the product's vocabulary and the shipped defaults say how the common actions reach them (`services/moment_actions.py`). What no @@ -20,8 +20,8 @@ class MomentMapping(Base, CreatedAtMixin): say "not here", and saying so must survive an upgrade that ships the same default again. - Text rather than a CHECK on `effect`, for retrieval_tuning_events' reason - (rule 36): the service validates it, and a constraint would buy nothing but + Text rather than a CHECK on `effect`, for retrieval_tuning_events' reason: + the service validates it, and a constraint would buy nothing but a migration the day a third effect is wanted. ONE ROW PER (user, tool, match, moment), so mapping the same thing twice diff --git a/src/scribe/models/note_usage.py b/src/scribe/models/note_usage.py index 1685469b..c119c95c 100644 --- a/src/scribe/models/note_usage.py +++ b/src/scribe/models/note_usage.py @@ -54,23 +54,22 @@ class NoteUsageEvent(Base, CreatedAtMixin): # A CONVENTION, not a fixed vocabulary: `mcp_` for an agent call, # `rest_` for a human opening a detail view, and a bare name for a # hook or background surface ('auto_inject', 'write_path_place', - # 'write_path_semantic'). This comment deliberately no longer lists the - # members — the previous list had gone stale, naming 'rest_note' that - # nothing wrote while omitting sources that existed, and a half-true - # enumeration reads as authoritative in exactly the way that misleads - # (#2476). `grep -rn record_pulled\\\|record_surfaced src/` is the + # 'write_path_semantic'). This comment deliberately does not list every + # member — such a list goes stale, naming sources nothing writes while + # omitting ones that exist, and a half-true enumeration reads as + # authoritative in exactly the way that misleads. `grep -rn record_pulled\\\|record_surfaced src/` is the # authoritative list, and unlike a comment it cannot drift. # # The mcp_/rest_ split is load-bearing. "Is this dead weight?" is served by # any pull; "was that injected line useful?" is served by AGENT pulls only, - # so never aggregate across the prefix without saying why (#1038, #2085). + # so never aggregate across the prefix without saying why. source: Mapped[str] = mapped_column(Text, nullable=False) # The project the READER was in when this happened — not the project the # record belongs to, which is already on `notes.project_id`. The whole # point is the comparison between the two: a record opened somewhere other # than where it was written is the evidence that it TRANSFERRED, which is - # the claim the lesson kind rests on (milestone 385) and the one thing + # the claim the lesson kind rests on and the one thing # RetrievalLog could half-answer and this table could not answer at all. # # Nullable, and null is ordinary rather than historical. A surfacing arm diff --git a/src/scribe/models/project.py b/src/scribe/models/project.py index 140b5ec5..6337e6b0 100644 --- a/src/scribe/models/project.py +++ b/src/scribe/models/project.py @@ -28,7 +28,7 @@ class Project(Base, TimestampMixin, SoftDeleteMixin): design_system_id: Mapped[int | None] = mapped_column( BigInteger, ForeignKey("design_systems.id", ondelete="SET NULL"), nullable=True ) - # The per-project forge pin (#2778). NULL is the ordinary state: forge + # The per-project forge pin. NULL is the ordinary state: forge # reads resolve against the owner's keyring by repo host. When set, the # project's forge reads use ONLY this connection — an explicit, auditable # choice, constrained by the service layer to a connection the project @@ -37,7 +37,7 @@ class Project(Base, TimestampMixin, SoftDeleteMixin): BigInteger, ForeignKey("forge_connections.id", ondelete="SET NULL"), nullable=True, ) - # The inception record (milestone 297): what this project was decided to + # The inception record: what this project was decided to # inherit, when, and through which door — {decided_at, decided_by, via, # choices: {design_system_id, seed_systems}}. NULL means nobody has # decided yet, and enter_project asks; the effects themselves live in diff --git a/src/scribe/models/repo_binding.py b/src/scribe/models/repo_binding.py index 7be94eb6..3c917884 100644 --- a/src/scribe/models/repo_binding.py +++ b/src/scribe/models/repo_binding.py @@ -28,7 +28,7 @@ class RepoBinding(Base, TimestampMixin): Integer, ForeignKey("projects.id", ondelete="CASCADE"), nullable=False ) repo_key: Mapped[str] = mapped_column(Text, nullable=False) - # The branch the coverage refresh reads for this binding (#2873); NULL = + # The branch the coverage refresh reads for this binding; NULL = # the forge's default branch. Chosen at bind time so a dev-first project # can have its ledger follow dev instead of waiting for the merge. ref: Mapped[str | None] = mapped_column(Text, nullable=True) diff --git a/src/scribe/models/retrieval_judgment.py b/src/scribe/models/retrieval_judgment.py index cb630f74..7a695cad 100644 --- a/src/scribe/models/retrieval_judgment.py +++ b/src/scribe/models/retrieval_judgment.py @@ -11,12 +11,11 @@ VERDICTS = (ON_POINT, ADJACENT, UNRELATED) class RetrievalJudgment(Base, CreatedAtMixin): - """One reviewer's verdict on one candidate of one logged retrieval call - (#4772). + """One reviewer's verdict on one candidate of one logged retrieval call. The relevance half the usage tables cannot hold. `note_usage_events` says - whether a line was OPENED, and since every menu line carries its matched - passage (#4364) "not opened" covers an unrelated line, a line whose passage + whether a line was OPENED, and because every menu line carries its matched + passage, "not opened" covers an unrelated line, a line whose passage was enough, and a line the reader already had. Only someone reading the query beside the line can tell those apart, so this records that reading — with its reason, because a verdict nobody can re-read is a threshold. diff --git a/src/scribe/models/retrieval_log.py b/src/scribe/models/retrieval_log.py index 2eb08f90..ca3ca62e 100644 --- a/src/scribe/models/retrieval_log.py +++ b/src/scribe/models/retrieval_log.py @@ -48,8 +48,8 @@ class RetrievalLog(Base): # suppressed". `result_count == 0` alone conflates two different events — # the ranker found nothing above threshold, and the ranker found something # the reader already had — and only the first says a threshold is too high. - # Reading a zero as a ranker decline is how #3311 mis-scoped a milestone; - # an unmeasured value that renders as 0 is the same mistake with a nicer + # Reading a zero as a ranker decline sends a reader to move a floor that + # was never involved; an unmeasured value that renders as 0 is the same mistake with a nicer # face, so surfaces that filter INSIDE the search leave this null. suppressed_count: Mapped[int | None] = mapped_column(Integer, nullable=True) top_score: Mapped[float | None] = mapped_column(Float, nullable=True) @@ -58,12 +58,12 @@ class RetrievalLog(Base): # against `top_score`, which is the best it DID offer. They are equal on # any call that returned something, and only this one exists on a call # that returned nothing, which is the only place a bar can be judged from - # (#3670). Null means the caller did not measure it, never "nothing was + # Null means the caller did not measure it, never "nothing was # close": a 0.0 there would read as a corpus with no relevant records at # all, which is an artifact standing in for a measurement. best_available_score: Mapped[float | None] = mapped_column(Float, nullable=True) # WHICH record scored that, so a reader can judge what the bar refused - # rather than only how close it came (#3807). Written from the same ranked + # rather than only how close it came. Written from the same ranked # candidate as the score above — the two describing different records would # be worse than no id at all, because it invites judging the wrong one. # diff --git a/src/scribe/models/rule_moment_judgment.py b/src/scribe/models/rule_moment_judgment.py index 8cdbf14d..7d716f22 100644 --- a/src/scribe/models/rule_moment_judgment.py +++ b/src/scribe/models/rule_moment_judgment.py @@ -10,7 +10,7 @@ from scribe.models.lesson_rule_link import CONFIRMED, LINK_STATES, REJECTED, SUG # The same three states as a lesson's link to a rule, for the same reasons — # re-exported so a reader of this table need not know where they were first -# named. CHECK ck_rule_moment_judgments_state (migration 0118, rule 36). +# named. CHECK ck_rule_moment_judgments_state (migration 0118). JUDGMENT_STATES = LINK_STATES __all__ = [ "CONFIRMED", "JUDGMENT_STATES", "NO_MOMENT", "REJECTED", "SUGGESTED", @@ -31,7 +31,7 @@ SOURCES = ("pass", "signal", "edit") class RuleMomentJudgment(Base, CreatedAtMixin): - """Whether a rule belongs on a moment, and who said so (milestone 458 step 7). + """Whether a rule belongs on a moment, and who said so. A mount (`rule_moments`) is the current answer; this is the reasoning behind it and the answers that are NOT mounts. One row per (rule, moment): @@ -60,7 +60,7 @@ class RuleMomentJudgment(Base, CreatedAtMixin): note: Mapped[str | None] = mapped_column(Text, nullable=True) # The co-occurrence evidence (signal source), in lesson_rules' shape. evidence: Mapped[dict | None] = mapped_column(JSONB, nullable=True) - # The other direction (step 7b, migration 0119): sessions saying this + # The other direction (migration 0119): sessions saying this # MOUNTED rule arrived at the moment and did not apply. lesson_rules' # evidence shape keyed per day, plus the reasons given and the actions # that reached the moment; `kept_at` once the operator kept the mount. diff --git a/src/scribe/models/system.py b/src/scribe/models/system.py index 6788c34f..3c46df86 100644 --- a/src/scribe/models/system.py +++ b/src/scribe/models/system.py @@ -25,7 +25,7 @@ class System(Base, TimestampMixin, SoftDeleteMixin): Integer, ForeignKey("projects.id", ondelete="CASCADE") ) name: Mapped[str] = mapped_column(Text, default="", server_default="") - # The GLOBAL area this local System is an instance of (milestone 307). + # The GLOBAL area this local System is an instance of. # Nullable and SET NULL on purpose: the catalog is a convergence aid, not a # gate — an unmapped System is fully usable, and retiring a canonical entry # must never take a project's System with it. The local `name` is NEVER @@ -40,7 +40,7 @@ class System(Base, TimestampMixin, SoftDeleteMixin): status: Mapped[str] = mapped_column(Text, default="active", server_default="active") order_index: Mapped[int] = mapped_column(Integer, default=0, server_default="0") # The files that ARE this area, as globs relative to the repo root - # (milestone 444). The description says what the area is FOR; these say + # The description says what the area is FOR; these say # where it lives, so a command or edit touching those files can be # resolved to the System — and its rulings — without a similarity search. # NOT NULL with a `[]` default for the reason DesignToken.supersedes gives: diff --git a/src/scribe/models/system_usage.py b/src/scribe/models/system_usage.py index 48e2742d..01dc3a7e 100644 --- a/src/scribe/models/system_usage.py +++ b/src/scribe/models/system_usage.py @@ -10,7 +10,7 @@ PULLED = "pulled" class SystemUsageEvent(Base, CreatedAtMixin): """One row per time a System's rulings were SHOWN to the agent because its - files were touched, or the System was PULLED in full (milestone 444). + files were touched, or the System was PULLED in full. The third of the usage tables, after `note_usage_events` and `rule_usage_events`, and separate from both for the reason the rule twin diff --git a/src/scribe/routes/admin.py b/src/scribe/routes/admin.py index 43e98641..5e942653 100644 --- a/src/scribe/routes/admin.py +++ b/src/scribe/routes/admin.py @@ -172,8 +172,8 @@ async def test_smtp(): return jsonify({"error": str(e)}), 500 -# The forge CONFIG moved to per-user keyring rows (#2778, Settings → Git -# forges); what stays admin is the webhook secret, because the push endpoint +# The forge CONFIG is per-user keyring rows (Settings → Git forges); what +# stays admin is the webhook secret, because the push endpoint # is one URL per instance and authenticates deliveries, not users. @@ -329,13 +329,13 @@ async def run_db_maintenance_now(): return jsonify(summary) -# ── Scheduled backups (milestone 523) ──────────────────────────────────────── +# ── Scheduled backups ──────────────────────────────────────────────────────── # One dated, uncompressed tar per run to a folder or an S3 bucket — see # services/backup_file.py for why it is shaped that way, backup_destination.py # for where it goes, backup_job.py for when. # How long "Test" waits on the destination. A folder on a hung NFS mount has -# no timeout of its own (rule 156), and the request must come back either way. +# no timeout of its own, and the request must come back either way. _BACKUP_CHECK_TIMEOUT = 45.0 diff --git a/src/scribe/routes/canonical_systems.py b/src/scribe/routes/canonical_systems.py index 245a64b6..2f2ee70e 100644 --- a/src/scribe/routes/canonical_systems.py +++ b/src/scribe/routes/canonical_systems.py @@ -1,5 +1,5 @@ """Canonical-system routes — the GLOBAL area vocabulary, and the mapping of a -project's Systems onto it (milestone 307, decision note 3026). +project's Systems onto it. Two shapes live here because they are two halves of one idea: diff --git a/src/scribe/routes/design_systems.py b/src/scribe/routes/design_systems.py index ceff5aee..2ae89b25 100644 --- a/src/scribe/routes/design_systems.py +++ b/src/scribe/routes/design_systems.py @@ -1,4 +1,4 @@ -"""Design system + token REST endpoints (milestone #254 step 4). +"""Design system + token REST endpoints. Wraps `services/design_systems.py`, which owns the ACL and the cycle guard. Design systems are owner-scoped top-level records rather than project-scoped diff --git a/src/scribe/routes/family.py b/src/scribe/routes/family.py index acd3cafb..daf90a0e 100644 --- a/src/scribe/routes/family.py +++ b/src/scribe/routes/family.py @@ -1,11 +1,11 @@ -"""Family canon routes — the web door to the promotion engine (milestone 463). +"""Family canon routes — the web door to the promotion engine. The agent decides promotions and each project's answers through the MCP tools; this door exists so a person can READ every decision and its reasons — the ideas, the adoption matrix, the log — and retire an idea or undo a decision when they disagree. The promote and propose endpoints are here for -parity with the agent's door (rule 33), and are recorded as the operator's. -Every write is gated on the idea's note in the service (rule 78). +parity with the agent's door, and are recorded as the operator's. +Every write is gated on the idea's note in the service. """ import logging diff --git a/src/scribe/routes/knowledge.py b/src/scribe/routes/knowledge.py index c7c79d3f..cd56d790 100644 --- a/src/scribe/routes/knowledge.py +++ b/src/scribe/routes/knowledge.py @@ -14,10 +14,9 @@ logger = logging.getLogger(__name__) knowledge_bp = Blueprint("knowledge", __name__, url_prefix="/api/knowledge") # Derived from the service's facet table, never re-listed here. This set was a -# hand-kept copy and had drifted three kinds behind it: it admitted `plan` -# (retired in 0066) and rejected `issue` (shipped in 0065, 435 rows) and -# `snippet` — so the browse surface could not filter to the kinds it was -# already rendering badges for (#3128). +# hand-kept copy drifts behind it — admitting retired kinds and rejecting new +# ones — until the browse surface cannot filter to the kinds it renders +# badges for. _VALID_TYPES = FACET_TYPES _VALID_SORTS = {"modified", "created", "alpha", "type"} @@ -64,13 +63,10 @@ async def list_knowledge(): ) items = await label_shared_items(uid, items) - # The surfaced-vs-opened counts, on the list a person actually browses - # (#4230). `usage_for_notes` always worked on every note row, but only the - # snippet and rule lists ever attached it — and this is the ONLY lesson and - # note list in the UI, so those two kinds had the counter collected and - # shown nowhere. Attaching here rather than teaching `/api/lessons` a - # second time is what closes both holes at once: `/knowledge` is how notes, - # lessons and processes are all browsed. + # The surfaced-vs-opened counts, on the list a person actually browses. + # This is the ONLY lesson and note list in the UI — `/knowledge` is how + # notes, lessons and processes are all browsed — so attaching here is what + # shows those kinds' counters at all. # # Mixed kinds is not a problem for this: usage keys on the note row, which # every facet of this feed is. diff --git a/src/scribe/routes/platforms.py b/src/scribe/routes/platforms.py index 0c040aad..69e97000 100644 --- a/src/scribe/routes/platforms.py +++ b/src/scribe/routes/platforms.py @@ -1,5 +1,5 @@ """Platform routes — the GLOBAL platform catalog, and which platforms a -project is (milestone 463 step 2). +project is. Two shapes, as with the canonical areas: diff --git a/src/scribe/routes/systems.py b/src/scribe/routes/systems.py index 75cb1b3c..b5becf96 100644 --- a/src/scribe/routes/systems.py +++ b/src/scribe/routes/systems.py @@ -44,7 +44,7 @@ async def list_systems_route(project_id: int): uid, project_id, include_archived=_truthy(request.args.get("include_archived")), ) counts = await systems_svc.open_issue_counts_by_system(uid, project_id) - # How often each area's rulings reached a session (#4769) — one GROUP BY + # How often each area's rulings reached a session — one GROUP BY # for the page, and a zero shape for an area nothing has touched. usage = await system_usage_svc.usage_for_systems([s.id for s in systems]) out = [] @@ -67,9 +67,8 @@ async def create_system_route(project_id: int): data = await request.get_json() or {} if not (data.get("name") or "").strip(): return jsonify({"error": "name is required"}), 400 - # The same gate the MCP door enforces. It lived only in the tool layer - # until now, which is exactly how the web UI shipped without gates the - # agent surface had (#2482) — one service call, one answer (rule 33). + # The same gate the MCP door enforces — one service call, one answer, so + # neither door ships without a gate the other has. assessment = await systems_svc.assess_system_name(uid, project_id, data["name"]) duplicate = assessment["duplicate"] if duplicate and not data.get("force"): diff --git a/src/scribe/routes/tasks.py b/src/scribe/routes/tasks.py index 167fc45c..2c4ea249 100644 --- a/src/scribe/routes/tasks.py +++ b/src/scribe/routes/tasks.py @@ -242,9 +242,8 @@ async def update_task_route(task_id: int): if "tags" in data: fields["tags"] = data["tags"] - # Re-filing a task's kind. The editor has always SENT this field and the - # route silently dropped it — reporting "Task saved" and reverting on the - # next load (#3129). Validated at the same layer as status and priority so + # Re-filing a task's kind. The editor sends this field; dropping it would + # report "Task saved" and revert on the next load. Validated at the same layer as status and priority so # an unrecognised value is a 400 rather than a database CHECK violation. if "kind" in data: try: