docs(6206): the remaining models, routes, mcp/tools/repos and config comments drop citations
A shipped tool docstring (mcp/tools/repos bind_repo) cited this instance's "rule 1: dev is home"; it now says what a dev-branch project should do. routes/knowledge, routes/tasks, routes/systems and models/retrieval_log state the property each guards rather than the bug that once broke it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -60,9 +60,9 @@ class Config:
|
|||||||
# the MCP layer doesn't proxy web search (Claude has its own).
|
# the MCP layer doesn't proxy web search (Claude has its own).
|
||||||
SEARXNG_URL: str = os.environ.get("SEARXNG_URL", "")
|
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
|
# 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
|
# 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
|
# 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.
|
# of the database. A stored row for the same host wins over the env entry.
|
||||||
|
|||||||
@@ -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):
|
The binding is also what the shape ledger reads (refresh_pattern_coverage):
|
||||||
`ref` names the branch it follows. Default (""): the repo's default branch
|
`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
|
— which means the ledger only sees work after a merge. A project that
|
||||||
(rule 1: dev is home) should bind with ref="dev" so classification follows
|
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;
|
the push, not the merge. Re-binding with ref="" keeps the standing ref;
|
||||||
pass ref="-" to clear it back to the default branch.
|
pass ref="-" to clear it back to the default branch.
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from scribe.models.base import SoftDeleteMixin, TimestampMixin, iso
|
|||||||
|
|
||||||
class CanonicalSystem(Base, TimestampMixin, SoftDeleteMixin):
|
class CanonicalSystem(Base, TimestampMixin, SoftDeleteMixin):
|
||||||
"""A GLOBAL area name — the shared vocabulary every project's Systems
|
"""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
|
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
|
project can reference one: a rule that applies across projects has no way
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ class DesignToken(Base, TimestampMixin, SoftDeleteMixin):
|
|||||||
# and the token's own value are usually different colours entirely. No
|
# and the token's own value are usually different colours entirely. No
|
||||||
# value-matching rule could ever connect them.
|
# 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.
|
# literals live in component CSS, which the panel cannot see and says so.
|
||||||
supersedes: Mapped[list] = mapped_column(
|
supersedes: Mapped[list] = mapped_column(
|
||||||
JSONB, nullable=False, default=list, server_default=text("'[]'::jsonb")
|
JSONB, nullable=False, default=list, server_default=text("'[]'::jsonb")
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from scribe.models.base import TimestampMixin, iso
|
|||||||
|
|
||||||
|
|
||||||
class ForgeConnection(Base, TimestampMixin):
|
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
|
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
|
forge read for a project runs on the PROJECT OWNER's set, resolved by the
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from scribe.models.base import CreatedAtMixin, iso
|
|||||||
|
|
||||||
|
|
||||||
class MomentMapping(Base, CreatedAtMixin):
|
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 moments are the product's vocabulary and the shipped defaults say how
|
||||||
the common actions reach them (`services/moment_actions.py`). What no
|
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
|
say "not here", and saying so must survive an upgrade that ships the same
|
||||||
default again.
|
default again.
|
||||||
|
|
||||||
Text rather than a CHECK on `effect`, for retrieval_tuning_events' reason
|
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
|
the service validates it, and a constraint would buy nothing but
|
||||||
a migration the day a third effect is wanted.
|
a migration the day a third effect is wanted.
|
||||||
|
|
||||||
ONE ROW PER (user, tool, match, moment), so mapping the same thing twice
|
ONE ROW PER (user, tool, match, moment), so mapping the same thing twice
|
||||||
|
|||||||
@@ -54,23 +54,22 @@ class NoteUsageEvent(Base, CreatedAtMixin):
|
|||||||
# A CONVENTION, not a fixed vocabulary: `mcp_<tool>` for an agent call,
|
# A CONVENTION, not a fixed vocabulary: `mcp_<tool>` for an agent call,
|
||||||
# `rest_<kind>` for a human opening a detail view, and a bare name for a
|
# `rest_<kind>` for a human opening a detail view, and a bare name for a
|
||||||
# hook or background surface ('auto_inject', 'write_path_place',
|
# hook or background surface ('auto_inject', 'write_path_place',
|
||||||
# 'write_path_semantic'). This comment deliberately no longer lists the
|
# 'write_path_semantic'). This comment deliberately does not list every
|
||||||
# members — the previous list had gone stale, naming 'rest_note' that
|
# member — such a list goes stale, naming sources nothing writes while
|
||||||
# nothing wrote while omitting sources that existed, and a half-true
|
# omitting ones that exist, and a half-true enumeration reads as
|
||||||
# enumeration reads as authoritative in exactly the way that misleads
|
# authoritative in exactly the way that misleads. `grep -rn record_pulled\\\|record_surfaced src/` is the
|
||||||
# (#2476). `grep -rn record_pulled\\\|record_surfaced src/` is the
|
|
||||||
# authoritative list, and unlike a comment it cannot drift.
|
# authoritative list, and unlike a comment it cannot drift.
|
||||||
#
|
#
|
||||||
# The mcp_/rest_ split is load-bearing. "Is this dead weight?" is served by
|
# 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,
|
# 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)
|
source: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
|
||||||
# The project the READER was in when this happened — not the project the
|
# 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
|
# record belongs to, which is already on `notes.project_id`. The whole
|
||||||
# point is the comparison between the two: a record opened somewhere other
|
# 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
|
# 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.
|
# RetrievalLog could half-answer and this table could not answer at all.
|
||||||
#
|
#
|
||||||
# Nullable, and null is ordinary rather than historical. A surfacing arm
|
# Nullable, and null is ordinary rather than historical. A surfacing arm
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class Project(Base, TimestampMixin, SoftDeleteMixin):
|
|||||||
design_system_id: Mapped[int | None] = mapped_column(
|
design_system_id: Mapped[int | None] = mapped_column(
|
||||||
BigInteger, ForeignKey("design_systems.id", ondelete="SET NULL"), nullable=True
|
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
|
# reads resolve against the owner's keyring by repo host. When set, the
|
||||||
# project's forge reads use ONLY this connection — an explicit, auditable
|
# project's forge reads use ONLY this connection — an explicit, auditable
|
||||||
# choice, constrained by the service layer to a connection the project
|
# 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"),
|
BigInteger, ForeignKey("forge_connections.id", ondelete="SET NULL"),
|
||||||
nullable=True,
|
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,
|
# inherit, when, and through which door — {decided_at, decided_by, via,
|
||||||
# choices: {design_system_id, seed_systems}}. NULL means nobody has
|
# choices: {design_system_id, seed_systems}}. NULL means nobody has
|
||||||
# decided yet, and enter_project asks; the effects themselves live in
|
# decided yet, and enter_project asks; the effects themselves live in
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class RepoBinding(Base, TimestampMixin):
|
|||||||
Integer, ForeignKey("projects.id", ondelete="CASCADE"), nullable=False
|
Integer, ForeignKey("projects.id", ondelete="CASCADE"), nullable=False
|
||||||
)
|
)
|
||||||
repo_key: Mapped[str] = mapped_column(Text, 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
|
# 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.
|
# can have its ledger follow dev instead of waiting for the merge.
|
||||||
ref: Mapped[str | None] = mapped_column(Text, nullable=True)
|
ref: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
|||||||
@@ -11,12 +11,11 @@ VERDICTS = (ON_POINT, ADJACENT, UNRELATED)
|
|||||||
|
|
||||||
|
|
||||||
class RetrievalJudgment(Base, CreatedAtMixin):
|
class RetrievalJudgment(Base, CreatedAtMixin):
|
||||||
"""One reviewer's verdict on one candidate of one logged retrieval call
|
"""One reviewer's verdict on one candidate of one logged retrieval call.
|
||||||
(#4772).
|
|
||||||
|
|
||||||
The relevance half the usage tables cannot hold. `note_usage_events` says
|
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
|
whether a line was OPENED, and because every menu line carries its matched
|
||||||
passage (#4364) "not opened" covers an unrelated line, a line whose passage
|
passage, "not opened" covers an unrelated line, a line whose passage
|
||||||
was enough, and a line the reader already had. Only someone reading the
|
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 —
|
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.
|
with its reason, because a verdict nobody can re-read is a threshold.
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ class RetrievalLog(Base):
|
|||||||
# suppressed". `result_count == 0` alone conflates two different events —
|
# suppressed". `result_count == 0` alone conflates two different events —
|
||||||
# the ranker found nothing above threshold, and the ranker found something
|
# 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.
|
# 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;
|
# Reading a zero as a ranker decline sends a reader to move a floor that
|
||||||
# an unmeasured value that renders as 0 is the same mistake with a nicer
|
# 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.
|
# face, so surfaces that filter INSIDE the search leave this null.
|
||||||
suppressed_count: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
suppressed_count: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||||
top_score: Mapped[float | None] = mapped_column(Float, 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
|
# 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
|
# 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
|
# 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
|
# 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.
|
# all, which is an artifact standing in for a measurement.
|
||||||
best_available_score: Mapped[float | None] = mapped_column(Float, nullable=True)
|
best_available_score: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||||
# WHICH record scored that, so a reader can judge what the bar refused
|
# 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
|
# 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.
|
# be worse than no id at all, because it invites judging the wrong one.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -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 —
|
# 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
|
# 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
|
JUDGMENT_STATES = LINK_STATES
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"CONFIRMED", "JUDGMENT_STATES", "NO_MOMENT", "REJECTED", "SUGGESTED",
|
"CONFIRMED", "JUDGMENT_STATES", "NO_MOMENT", "REJECTED", "SUGGESTED",
|
||||||
@@ -31,7 +31,7 @@ SOURCES = ("pass", "signal", "edit")
|
|||||||
|
|
||||||
|
|
||||||
class RuleMomentJudgment(Base, CreatedAtMixin):
|
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
|
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):
|
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)
|
note: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
# The co-occurrence evidence (signal source), in lesson_rules' shape.
|
# The co-occurrence evidence (signal source), in lesson_rules' shape.
|
||||||
evidence: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
|
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'
|
# MOUNTED rule arrived at the moment and did not apply. lesson_rules'
|
||||||
# evidence shape keyed per day, plus the reasons given and the actions
|
# evidence shape keyed per day, plus the reasons given and the actions
|
||||||
# that reached the moment; `kept_at` once the operator kept the mount.
|
# that reached the moment; `kept_at` once the operator kept the mount.
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class System(Base, TimestampMixin, SoftDeleteMixin):
|
|||||||
Integer, ForeignKey("projects.id", ondelete="CASCADE")
|
Integer, ForeignKey("projects.id", ondelete="CASCADE")
|
||||||
)
|
)
|
||||||
name: Mapped[str] = mapped_column(Text, default="", server_default="")
|
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
|
# 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
|
# 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
|
# 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")
|
status: Mapped[str] = mapped_column(Text, default="active", server_default="active")
|
||||||
order_index: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
|
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
|
# 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
|
# where it lives, so a command or edit touching those files can be
|
||||||
# resolved to the System — and its rulings — without a similarity search.
|
# resolved to the System — and its rulings — without a similarity search.
|
||||||
# NOT NULL with a `[]` default for the reason DesignToken.supersedes gives:
|
# NOT NULL with a `[]` default for the reason DesignToken.supersedes gives:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ PULLED = "pulled"
|
|||||||
|
|
||||||
class SystemUsageEvent(Base, CreatedAtMixin):
|
class SystemUsageEvent(Base, CreatedAtMixin):
|
||||||
"""One row per time a System's rulings were SHOWN to the agent because its
|
"""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
|
The third of the usage tables, after `note_usage_events` and
|
||||||
`rule_usage_events`, and separate from both for the reason the rule twin
|
`rule_usage_events`, and separate from both for the reason the rule twin
|
||||||
|
|||||||
@@ -172,8 +172,8 @@ async def test_smtp():
|
|||||||
return jsonify({"error": str(e)}), 500
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
# The forge CONFIG moved to per-user keyring rows (#2778, Settings → Git
|
# The forge CONFIG is per-user keyring rows (Settings → Git forges); what
|
||||||
# forges); what stays admin is the webhook secret, because the push endpoint
|
# stays admin is the webhook secret, because the push endpoint
|
||||||
# is one URL per instance and authenticates deliveries, not users.
|
# is one URL per instance and authenticates deliveries, not users.
|
||||||
|
|
||||||
|
|
||||||
@@ -329,13 +329,13 @@ async def run_db_maintenance_now():
|
|||||||
return jsonify(summary)
|
return jsonify(summary)
|
||||||
|
|
||||||
|
|
||||||
# ── Scheduled backups (milestone 523) ────────────────────────────────────────
|
# ── Scheduled backups ────────────────────────────────────────────────────────
|
||||||
# One dated, uncompressed tar per run to a folder or an S3 bucket — see
|
# 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
|
# services/backup_file.py for why it is shaped that way, backup_destination.py
|
||||||
# for where it goes, backup_job.py for when.
|
# for where it goes, backup_job.py for when.
|
||||||
|
|
||||||
# How long "Test" waits on the destination. A folder on a hung NFS mount has
|
# 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
|
_BACKUP_CHECK_TIMEOUT = 45.0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""Canonical-system routes — the GLOBAL area vocabulary, and the mapping of a
|
"""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:
|
Two shapes live here because they are two halves of one idea:
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
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
|
Design systems are owner-scoped top-level records rather than project-scoped
|
||||||
|
|||||||
@@ -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
|
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
|
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
|
— 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
|
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.
|
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 (rule 78).
|
Every write is gated on the idea's note in the service.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,9 @@ logger = logging.getLogger(__name__)
|
|||||||
knowledge_bp = Blueprint("knowledge", __name__, url_prefix="/api/knowledge")
|
knowledge_bp = Blueprint("knowledge", __name__, url_prefix="/api/knowledge")
|
||||||
|
|
||||||
# Derived from the service's facet table, never re-listed here. This set was a
|
# 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`
|
# hand-kept copy drifts behind it — admitting retired kinds and rejecting new
|
||||||
# (retired in 0066) and rejected `issue` (shipped in 0065, 435 rows) and
|
# ones — until the browse surface cannot filter to the kinds it renders
|
||||||
# `snippet` — so the browse surface could not filter to the kinds it was
|
# badges for.
|
||||||
# already rendering badges for (#3128).
|
|
||||||
_VALID_TYPES = FACET_TYPES
|
_VALID_TYPES = FACET_TYPES
|
||||||
_VALID_SORTS = {"modified", "created", "alpha", "type"}
|
_VALID_SORTS = {"modified", "created", "alpha", "type"}
|
||||||
|
|
||||||
@@ -64,13 +63,10 @@ async def list_knowledge():
|
|||||||
)
|
)
|
||||||
|
|
||||||
items = await label_shared_items(uid, items)
|
items = await label_shared_items(uid, items)
|
||||||
# The surfaced-vs-opened counts, on the list a person actually browses
|
# 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
|
# This is the ONLY lesson and note list in the UI — `/knowledge` is how
|
||||||
# snippet and rule lists ever attached it — and this is the ONLY lesson and
|
# notes, lessons and processes are all browsed — so attaching here is what
|
||||||
# note list in the UI, so those two kinds had the counter collected and
|
# shows those kinds' counters at all.
|
||||||
# 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.
|
|
||||||
#
|
#
|
||||||
# Mixed kinds is not a problem for this: usage keys on the note row, which
|
# Mixed kinds is not a problem for this: usage keys on the note row, which
|
||||||
# every facet of this feed is.
|
# every facet of this feed is.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""Platform routes — the GLOBAL platform catalog, and which platforms a
|
"""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:
|
Two shapes, as with the canonical areas:
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ async def list_systems_route(project_id: int):
|
|||||||
uid, project_id, include_archived=_truthy(request.args.get("include_archived")),
|
uid, project_id, include_archived=_truthy(request.args.get("include_archived")),
|
||||||
)
|
)
|
||||||
counts = await systems_svc.open_issue_counts_by_system(uid, project_id)
|
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.
|
# 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])
|
usage = await system_usage_svc.usage_for_systems([s.id for s in systems])
|
||||||
out = []
|
out = []
|
||||||
@@ -67,9 +67,8 @@ async def create_system_route(project_id: int):
|
|||||||
data = await request.get_json() or {}
|
data = await request.get_json() or {}
|
||||||
if not (data.get("name") or "").strip():
|
if not (data.get("name") or "").strip():
|
||||||
return jsonify({"error": "name is required"}), 400
|
return jsonify({"error": "name is required"}), 400
|
||||||
# The same gate the MCP door enforces. It lived only in the tool layer
|
# The same gate the MCP door enforces — one service call, one answer, so
|
||||||
# until now, which is exactly how the web UI shipped without gates the
|
# neither door ships without a gate the other has.
|
||||||
# agent surface had (#2482) — one service call, one answer (rule 33).
|
|
||||||
assessment = await systems_svc.assess_system_name(uid, project_id, data["name"])
|
assessment = await systems_svc.assess_system_name(uid, project_id, data["name"])
|
||||||
duplicate = assessment["duplicate"]
|
duplicate = assessment["duplicate"]
|
||||||
if duplicate and not data.get("force"):
|
if duplicate and not data.get("force"):
|
||||||
|
|||||||
@@ -242,9 +242,8 @@ async def update_task_route(task_id: int):
|
|||||||
if "tags" in data:
|
if "tags" in data:
|
||||||
fields["tags"] = data["tags"]
|
fields["tags"] = data["tags"]
|
||||||
|
|
||||||
# Re-filing a task's kind. The editor has always SENT this field and the
|
# Re-filing a task's kind. The editor sends this field; dropping it would
|
||||||
# route silently dropped it — reporting "Task saved" and reverting on the
|
# report "Task saved" and revert on the next load. Validated at the same layer as status and priority so
|
||||||
# next load (#3129). Validated at the same layer as status and priority so
|
|
||||||
# an unrecognised value is a 400 rather than a database CHECK violation.
|
# an unrecognised value is a 400 rather than a database CHECK violation.
|
||||||
if "kind" in data:
|
if "kind" in data:
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user