Compare commits
2
Commits
79655fd19f
...
699a4cd986
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
699a4cd986 | ||
|
|
637ec66b79 |
@@ -3,11 +3,10 @@
|
||||
*
|
||||
* WHY THIS FILE EXISTS
|
||||
*
|
||||
* `.btn-primary` was defined five times, in five scoped stylesheets, and all
|
||||
* five had drifted: three paddings, three font sizes, three disabled opacities,
|
||||
* and one view with no disabled style at all (#2273). Nothing detected that,
|
||||
* because a scoped duplicate is invisible to every tool — it isn't a rule
|
||||
* violation, isn't a broken reference, and isn't a recorded snippet.
|
||||
* A recipe copied into several scoped stylesheets drifts — paddings, font
|
||||
* sizes, disabled states — and nothing detects it, because a scoped duplicate
|
||||
* is invisible to every tool: it isn't a rule violation, isn't a broken
|
||||
* reference, and isn't a recorded snippet. Shared recipes live here instead.
|
||||
*
|
||||
* GEOMETRY LIVES HERE. Every value is a design-system token, so a palette or
|
||||
* scale change moves the buttons rather than stranding a copy that no longer
|
||||
@@ -301,9 +300,7 @@
|
||||
/* ── Page container ─────────────────────────────────────────────────────────
|
||||
The one wrapper a top-level view sits in: page width from the layout
|
||||
tokens, centred, clipped horizontally so a wide child (a kanban, a table)
|
||||
scrolls inside itself instead of the page. ProjectListView, ProjectView and
|
||||
SnippetListView each carried this rule under their own name until #2903
|
||||
(milestone 299). */
|
||||
scrolls inside itself instead of the page. */
|
||||
.page-container {
|
||||
max-width: var(--fs-layout-page-max);
|
||||
margin: 2rem auto;
|
||||
@@ -311,12 +308,11 @@
|
||||
overflow-x: clip;
|
||||
}
|
||||
|
||||
/* ── Form input (fs-surfaces, snippet #2336) ────────────────────────────────
|
||||
/* ── Form input (fs-surfaces) ───────────────────────────────────────────────
|
||||
Inputs sit DARKER than the page they're on — an inset well rather than a
|
||||
raised panel; that inversion is what makes a field read as writable. The
|
||||
design system's recipe, verbatim; width/box-sizing stay the caller's
|
||||
(an inline select and a full-width textarea differ there). Three scoped
|
||||
copies of an older input recipe were folded into this in #2903. */
|
||||
(an inline select and a full-width textarea differ there). */
|
||||
.fs-input {
|
||||
background: var(--fs-surface-page);
|
||||
border: var(--fs-border);
|
||||
@@ -331,8 +327,8 @@
|
||||
.fs-input:focus { outline: none; box-shadow: var(--fs-focus-ring); }
|
||||
.fs-input:disabled { opacity: var(--fs-disabled-opacity); cursor: not-allowed; }
|
||||
|
||||
/* Page scaffold + feedback text recipes (milestone 302, note 2917): name
|
||||
families the consumer map showed to be one recipe living in many views.
|
||||
/* Page scaffold + feedback text recipes: families that are one recipe
|
||||
living in many views.
|
||||
A view keeps only its deviation as a scoped remainder/override. */
|
||||
.page-header {
|
||||
display: flex;
|
||||
@@ -355,9 +351,8 @@
|
||||
/* --- usage badge ----------------------------------------------------------
|
||||
"surfaced N×, opened M×" on a list row, for any record kind the retrieval
|
||||
surfaces can choose: snippets and notes from note_usage_events, rules from
|
||||
rule_usage_events. Promoted here from SnippetListView's scoped block when
|
||||
the rule list needed the same chip (milestone 333 step 5) — a second scoped
|
||||
copy is how the ninth duplicated CSS family starts (#3207).
|
||||
rule_usage_events. Shared here because a second scoped copy is how a
|
||||
duplicated CSS family starts.
|
||||
|
||||
Geometry and colour only. A view keeps its own spacing as a scoped
|
||||
remainder, the way it does for every other recipe in this file. */
|
||||
|
||||
@@ -48,7 +48,7 @@ interface KnowledgeItem {
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
/** Surfaced-vs-opened counts, zero-filled by the route for EVERY row, so
|
||||
* "never surfaced" is a value here rather than a missing field (#4230).
|
||||
* "never surfaced" is a value here rather than a missing field.
|
||||
* `UsageBadge` renders nothing at all below one surfacing. */
|
||||
usage?: RecordUsage | null;
|
||||
// Set only when another user owns this record — their suggestion, not one of
|
||||
@@ -69,10 +69,9 @@ interface KnowledgeItem {
|
||||
// a task KIND (`task` for any, else issue / spike) — because that is what this
|
||||
// feed actually holds.
|
||||
//
|
||||
// `plan` is still a valid facet at the API, for the 90 legacy plan-tasks, but
|
||||
// it has no chip: retired in 0066, it kept a chip of its own for longer than
|
||||
// `issue` — 17% of every task here — went without one (#3128). Those rows are
|
||||
// still reachable under Tasks, wearing a Plan badge.
|
||||
// `plan` is still a valid facet at the API, for legacy plan-tasks, but it has
|
||||
// no chip: plans are milestones now. Those rows are still reachable under
|
||||
// Tasks, wearing a Plan badge.
|
||||
type Facet = "" | "note" | "task" | "issue" | "spike" | "snippet" | "process" | "lesson";
|
||||
|
||||
// The facets that select TASKS. Kinds are subsets of `task`, so any of them
|
||||
@@ -93,7 +92,7 @@ const FACET_CHIPS: [Exclude<Facet, "">, string][] = [
|
||||
// The sweep is cross-cutting — a note that has gone false does not care which
|
||||
// facet it sits under — so it REPLACES the browse list rather than filtering
|
||||
// it. Filtering would mean the answer depended on which chip was active, which
|
||||
// is the under-reporting the sweep exists to prevent (milestone 317 step 4).
|
||||
// is the under-reporting the sweep exists to prevent.
|
||||
const sweepActive = ref(false);
|
||||
|
||||
// ─── Filter state ─────────────────────────────────────────────────────────────
|
||||
@@ -201,8 +200,8 @@ const items = ref<KnowledgeItem[]>([]);
|
||||
|
||||
// True only when a search actually returned matched passages — not merely when
|
||||
// the box has text in it. A keyword-only result set, or an embedder that is
|
||||
// down, carries `body_opening` rows, and claiming otherwise would be the same
|
||||
// species of lie this whole change is about (#4243).
|
||||
// down, carries `body_opening` rows, and labelling those as matched passages
|
||||
// would misstate why the row is there.
|
||||
const showsMatchedPassages = computed(
|
||||
() =>
|
||||
searchQuery.value.trim().length > 0 &&
|
||||
@@ -351,12 +350,8 @@ function isOverdue(item: KnowledgeItem): boolean {
|
||||
return new Date(item.due_date) < new Date(new Date().toDateString());
|
||||
}
|
||||
|
||||
// Each record kind opens in ITS OWN editor. A snippet used to fall through to
|
||||
// /notes/:id, whose save is a plain PATCH of the body — which left the snippet's
|
||||
// derived `data` mirror describing the previous version (#3128). The service now
|
||||
// recomposes the mirror either way, so this is no longer the guard; it is simply
|
||||
// that the note editor cannot edit a snippet's signature, language or locations,
|
||||
// and offering it as the way in was always wrong. Processes stay here on
|
||||
// Each record kind opens in ITS OWN editor: the note editor cannot edit a
|
||||
// snippet's signature, language or locations, so it is not the way in. Processes stay here on
|
||||
// purpose: they have no editor of their own and the note editor knows the type.
|
||||
function openItem(item: KnowledgeItem) {
|
||||
if (item.note_type === 'task') {
|
||||
@@ -666,7 +661,7 @@ onUnmounted(() => {
|
||||
class="shared-tag"
|
||||
:title="`Shared by ${item.owner ?? 'another user'} — their record, not yours`"
|
||||
>by {{ item.owner ?? "another user" }}</span>
|
||||
<!-- Surfaced-vs-opened (#4230). This is the only list in the UI
|
||||
<!-- Surfaced-vs-opened. This is the only list in the UI
|
||||
that browses notes and lessons, so it is the only place
|
||||
those two kinds can show the counter at all. The advice is
|
||||
looked up per row because this feed is mixed — the remedy
|
||||
@@ -1015,9 +1010,7 @@ onUnmounted(() => {
|
||||
.badge--note { background: color-mix(in srgb, var(--fs-accent) 15%, transparent); color: #7A6DA8; }
|
||||
.badge--task { background: rgba(212,160,23,0.15); color: #fbbf24; }
|
||||
.badge--plan { background: rgba(99,102,241,0.18); color: #818cf8; }
|
||||
/* Snippet and process are NEUTRAL on purpose. Both were unstyled — and the
|
||||
snippet had no label either, so all 90 of them rendered an empty chip in
|
||||
this feed (#3128). Giving them hues would put a third and fourth colour
|
||||
/* Snippet and process are NEUTRAL on purpose. Giving them hues would put a third and fourth colour
|
||||
beside KindBadge's warm/cool pair on the same card; a record type that
|
||||
isn't an alarm reads better as plain. Standard body pair, so the contrast
|
||||
is the one the palette already guarantees. */
|
||||
@@ -1158,11 +1151,9 @@ onUnmounted(() => {
|
||||
border-bottom: 1px solid var(--fs-border-color);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
/* RESTORED (#2444). The panel is a flex COLUMN and its header is
|
||||
`flex-shrink: 0`, so this is the item that takes the remaining height — and
|
||||
without it the `height: 100%` below resolves against `auto` and does
|
||||
nothing, which made the comment underneath a spec for a rule that could not
|
||||
work. `min-height: 0` is the companion that lets a flex item shrink under
|
||||
/* The panel is a flex COLUMN and its header is `flex-shrink: 0`, so this is
|
||||
the item that takes the remaining height — without it the `height: 100%`
|
||||
below resolves against `auto` and does nothing. `min-height: 0` is the companion that lets a flex item shrink under
|
||||
its content instead of overflowing the panel. */
|
||||
.graph-embed {
|
||||
flex: 1;
|
||||
|
||||
@@ -96,7 +96,7 @@ const tasksStore = useTasksStore();
|
||||
const project = ref<Project | null>(null);
|
||||
|
||||
// Design system the project is styled from. Loaded separately because an
|
||||
// install with none is the ordinary case (rule #115) and the picker simply
|
||||
// install with none is the ordinary case and the picker simply
|
||||
// doesn't render — a failed fetch must not take the project page with it.
|
||||
const designSystems = ref<DesignSystem[]>([]);
|
||||
const editDesignSystemId = ref<number | null>(null);
|
||||
@@ -189,8 +189,8 @@ function groupKey(group: MilestoneGroup): number {
|
||||
}
|
||||
|
||||
// A group's Done column starts COLLAPSED, to its header and count. Done work is
|
||||
// the part of a board nobody is reading, and listed in full it pushed the open
|
||||
// columns of every group below it off the screen (operator, 2026-09-15). Not
|
||||
// the part of a board nobody is reading, and listed in full it pushes the open
|
||||
// columns of every group below it off the screen. Not
|
||||
// persisted, matching the milestone collapse beside it: each load starts from
|
||||
// the same defaults.
|
||||
const expandedDone = ref<Set<number>>(new Set());
|
||||
@@ -418,7 +418,7 @@ async function loadTasks() {
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Say so. This used to swallow the error and leave an empty board, which is
|
||||
// Say so. A swallowed error leaves an empty board, which is
|
||||
// indistinguishable from a project with no tasks — the same "hidden with no
|
||||
// indicator" failure as the truncation above, one layer up.
|
||||
tasksError.value = "Could not load tasks. Refresh to try again.";
|
||||
@@ -465,7 +465,7 @@ async function loadNotes() {
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Pattern-library coverage (#2692) ─────────────────────────── */
|
||||
/* ── Pattern-library coverage ─────────────────────────────────── */
|
||||
|
||||
interface CoverageGap {
|
||||
dir: string;
|
||||
@@ -488,11 +488,11 @@ interface Coverage {
|
||||
computed_at: string;
|
||||
repos: { repo: string; ref: string; total: number; accounted: number }[];
|
||||
largest_gaps: CoverageGap[];
|
||||
// The mechanical proposer's standing (#2792): canon proposals awaiting an
|
||||
// The mechanical proposer's standing: canon proposals awaiting an
|
||||
// agent's confirm, and the biggest repeats-with-no-canon families.
|
||||
proposed?: number;
|
||||
derive_groups?: DeriveGroup[];
|
||||
// The divergence readout (#2793): button B where button A is canon, and
|
||||
// The divergence readout: button B where button A is canon, and
|
||||
// judged shapes whose bodies moved since they were judged.
|
||||
divergent?: number;
|
||||
divergence?: Divergence[];
|
||||
@@ -543,7 +543,7 @@ async function refreshCoverage() {
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Forge pin (#2778) — which of the OWNER's connections serves this
|
||||
/* ── Forge pin — which of the OWNER's connections serves this
|
||||
project. Owner-only UI: the select lists the viewer's own keyring, which
|
||||
is only the eligible set when the viewer IS the owner. ── */
|
||||
|
||||
@@ -737,7 +737,7 @@ async function confirmDelete() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Inception (milestone 297): the owner of an undecided project is asked
|
||||
<!-- Inception: the owner of an undecided project is asked
|
||||
what it inherits; once recorded, one line says what was decided. -->
|
||||
<InceptionCard
|
||||
v-if="project.inception == null && isProjectOwner"
|
||||
@@ -779,7 +779,7 @@ async function confirmDelete() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pattern-library coverage (#2692) — only when a forge is configured;
|
||||
<!-- Pattern-library coverage — only when a forge is configured;
|
||||
forge-less installs never see this card at all. -->
|
||||
<div v-if="coverageConfigured" class="coverage-card">
|
||||
<div class="coverage-head">
|
||||
@@ -874,7 +874,7 @@ async function confirmDelete() {
|
||||
the shape ledger and reports how many are classified against canon.
|
||||
</p>
|
||||
<p v-if="coverageError" class="coverage-error">{{ coverageError }}</p>
|
||||
<!-- Forge pin (#2778): owner-only, because the eligible set is the
|
||||
<!-- Forge pin: owner-only, because the eligible set is the
|
||||
owner's own keyring. Automatic = resolve by repo host. -->
|
||||
<div v-if="isProjectOwner && forgeOptions.length" class="coverage-forge-row">
|
||||
<label for="forge-pin" class="coverage-gaps-label">Forge</label>
|
||||
@@ -1200,7 +1200,7 @@ async function confirmDelete() {
|
||||
:design-system-id="project.design_system_id ?? null"
|
||||
/>
|
||||
|
||||
<!-- Family tab (milestone 463): which platforms this project is. -->
|
||||
<!-- Family tab: which platforms this project is. -->
|
||||
<ProjectFamilyTab
|
||||
v-if="activeTab === 'family'"
|
||||
:project-id="projectId"
|
||||
|
||||
@@ -20,7 +20,7 @@ const store = useSettingsStore();
|
||||
const authStore = useAuthStore();
|
||||
const toastStore = useToastStore();
|
||||
|
||||
// ── Shared areas (milestone 307) ────────────────────────────────────────
|
||||
// ── Shared areas ────────────────────────────────────────────────────────
|
||||
// The global vocabulary a project's Systems map onto. Admin-only to WRITE —
|
||||
// a global list anyone can extend stops being a shared list — but every user
|
||||
// reads it, which is why the catalog lives in a store rather than here.
|
||||
@@ -75,7 +75,7 @@ async function saveArea() {
|
||||
savingArea.value = false;
|
||||
}
|
||||
}
|
||||
// ── Platforms (milestone 463) ───────────────────────────────────────────
|
||||
// ── Platforms ───────────────────────────────────────────────────────────
|
||||
// The global list of what a project can be built on or ship as. Same split
|
||||
// as the areas: admin-only to write, readable by everyone. Markers are the
|
||||
// repo-relative globs that let a coverage refresh DETECT a platform; they
|
||||
@@ -157,46 +157,43 @@ const kbInjectThreshold = ref("0.55");
|
||||
const kbInjectTopK = ref("3");
|
||||
const kbWritePathEnabled = ref(true);
|
||||
// The write-path arm's OWN threshold, stricter than auto-inject's 0.55 above:
|
||||
// code embeddings sit on a much higher similarity floor than prose, so 0.55 let
|
||||
// unrelated code through (#2223). Shares top-k, not the threshold.
|
||||
// code embeddings sit on a much higher similarity floor than prose, so 0.55
|
||||
// lets unrelated code through. Shares top-k, not the threshold.
|
||||
const kbWritePathThreshold = ref("0.68");
|
||||
const kbRuleHintThreshold = ref("0.72");
|
||||
const kbCheckpointThreshold = ref("0.80");
|
||||
// The two ACT arms no longer share a bar (#3853). A write-path query is a code
|
||||
// payload; a pre-tool query is a shell command, often under a dozen words —
|
||||
// less text, less signal, lower scores for the same relevance. Measured at one
|
||||
// shared 0.72 the two behaved like different subsystems: the write-path arm
|
||||
// spoke on 37% of its calls, the command arm on 2% of 11,768.
|
||||
// The two ACT arms do not share a bar. A write-path query is a code payload; a
|
||||
// pre-tool query is a shell command, often under a dozen words — less text,
|
||||
// less signal, lower scores for the same relevance — so one shared bar makes
|
||||
// them behave like different subsystems.
|
||||
const kbToolRuleThreshold = ref("0.68");
|
||||
// And the prompt boundary is a third query shape again — the operator's own
|
||||
// prose rather than anything a tool produced (#3852).
|
||||
// prose rather than anything a tool produced.
|
||||
const kbPromptRuleThreshold = ref("0.72");
|
||||
// The reply arm (milestone 458): its bar is the bar at which a finished reply
|
||||
// The reply arm: its bar is the bar at which a finished reply
|
||||
// is HELD for one read, so it sits with the checkpoint, not with the hints.
|
||||
const kbReplyRuleThreshold = ref("0.8");
|
||||
// The BUDGETS, one per arm (#4102). Until this step only auto-inject had one
|
||||
// and every other arm's ceiling was a module constant nobody could reach — so
|
||||
// the only control an operator had over a noisy surface was to raise its bar,
|
||||
// which discards the surface's best candidates along with its worst. A budget
|
||||
// keeps the top of the ranking and drops the tail, which is what was wanted.
|
||||
// The BUDGETS, one per arm. Without one, the only control over a noisy surface
|
||||
// is raising its bar, which discards the surface's best candidates along with
|
||||
// its worst. A budget keeps the top of the ranking and drops the tail.
|
||||
const kbWritePathTopK = ref("3");
|
||||
const kbRuleHintTopK = ref("5");
|
||||
const kbToolRuleTopK = ref("5");
|
||||
const kbPromptRuleTopK = ref("3");
|
||||
const kbReplyRuleTopK = ref("1");
|
||||
// What has been changed about retrieval, newest first — the review surface for
|
||||
// changes the model made on the operator's behalf (#4102).
|
||||
// changes the model made on the operator's behalf.
|
||||
const tuningEvents = ref<TuningEvent[]>([]);
|
||||
const loadingTuning = ref(false);
|
||||
// Near-duplicate report floors, one per record kind (services/dedup.py).
|
||||
// Snippets are single-chunk, so their floor sits below the 0.90 write-time
|
||||
// gate and catches what it lets through. Notes/tasks are scored at chunk
|
||||
// grain (#280) — related families clear 0.90 easily — so their floor sits
|
||||
// grain — related families clear 0.90 easily — so their floor sits
|
||||
// above the gate to keep the report pointed at genuinely-alike records.
|
||||
const kbDupThresholdSnippet = ref("0.82");
|
||||
const kbDupThresholdNote = ref("0.93");
|
||||
const kbDupThresholdTask = ref("0.93");
|
||||
// The create gate's bars (#4385). Defaults mirror services/dedup.py.
|
||||
// The create gate's bars. Defaults mirror services/dedup.py.
|
||||
const kbGateThreshold = ref("0.9");
|
||||
const kbGateThresholdSnippet = ref("0.96");
|
||||
const kbGateThresholdLesson = ref("0.96");
|
||||
@@ -207,11 +204,6 @@ const kbPlanMatchThreshold = ref("0.80");
|
||||
const savingKbInject = ref(false);
|
||||
const kbInjectSaved = ref(false);
|
||||
|
||||
// think_enabled setting removed 2026-05-23. The chat+curator architecture
|
||||
// has tools=[] on the chat model; think on a no-tools conversational pass
|
||||
// is pure latency cost. See generation_task.py:run_generation comment for
|
||||
// the full reasoning.
|
||||
|
||||
function detectTimezone() {
|
||||
userTimezone.value = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
}
|
||||
@@ -246,7 +238,7 @@ async function saveRetention() {
|
||||
}
|
||||
}
|
||||
|
||||
// One entry in the retrieval tuning trail (#4102). Mirrors
|
||||
// One entry in the retrieval tuning trail. Mirrors
|
||||
// RetrievalTuningEvent.to_dict() — `old_value` is null for the first change to
|
||||
// a surface, which is a different event from moving off a value somebody chose
|
||||
// and renders differently below.
|
||||
@@ -262,7 +254,7 @@ interface TuningEvent {
|
||||
}
|
||||
|
||||
// What a dial's number was measured against, and whether that space has moved
|
||||
// (#4104). Mirrors the `calibration` block `current_settings` returns per dial.
|
||||
// Mirrors the `calibration` block `current_settings` returns per dial.
|
||||
// `stale` is nullable on purpose: a dial moved before Scribe recorded stamps is
|
||||
// UNKNOWN rather than fine, and rendering the two the same would hide the case
|
||||
// most likely to be wrong.
|
||||
@@ -335,7 +327,7 @@ async function loadTuningHistory() {
|
||||
}
|
||||
}
|
||||
|
||||
/** Who moved a dial. Three answers, not two (#4225).
|
||||
/** Who moved a dial. Three answers, not two.
|
||||
*
|
||||
* `release` means the shipped default itself changed between versions — no
|
||||
* user turned anything. Folding that into the `human ? 'you' : 'Claude'`
|
||||
@@ -445,9 +437,8 @@ async function saveKbInject() {
|
||||
// corpus confident enough to be read first" — and a number that moves
|
||||
// when another moves is a number nobody can reason about.
|
||||
kb_checkpoint_threshold: String(cpT),
|
||||
// A FOURTH and FIFTH bar, and they are separate keys on purpose: the
|
||||
// whole finding of #3853 is that one number cannot serve arms whose
|
||||
// queries are different shapes. Moving one must not move the others.
|
||||
// A FOURTH and FIFTH bar, and they are separate keys on purpose: one
|
||||
// number cannot serve arms whose queries are different shapes. Moving one must not move the others.
|
||||
kb_toolrule_threshold: String(trT),
|
||||
kb_promptrule_threshold: String(prT),
|
||||
// The reply backstop's own bar: it HOLDS a reply, so like the
|
||||
@@ -497,15 +488,15 @@ const exporting = ref(false);
|
||||
const restoring = ref(false);
|
||||
// Backup, export and restore walk the whole store, so they are slow BY DESIGN
|
||||
// and the client's ordinary 30s default would cut them off mid-work. They are
|
||||
// still bounded: rule 156 asks for a deadline, not a short one, and "no ceiling
|
||||
// at all" is what leaves a restore that died server-side spinning forever.
|
||||
// still bounded — every wait has a deadline, not necessarily a short one — and
|
||||
// "no ceiling at all" is what leaves a restore that died server-side spinning forever.
|
||||
const BULK_TRANSFER_TIMEOUT_MS = 10 * 60 * 1000;
|
||||
|
||||
function bulkDeadline(): AbortSignal {
|
||||
return AbortSignal.timeout(BULK_TRANSFER_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
// ── What's running (#3127 checklist 12) ─────────────────────────────────
|
||||
// ── What's running ──────────────────────────────────────────────────────
|
||||
// Three states kept apart, because collapsing any two of them is the defect
|
||||
// this readout exists to remove: `null` + no error = not asked yet (the Config
|
||||
// tab has not been opened); a payload = answered, with each ABSENT field shown
|
||||
@@ -822,7 +813,7 @@ const baseUrl = ref("");
|
||||
const savingBaseUrl = ref(false);
|
||||
const baseUrlSaved = ref(false);
|
||||
|
||||
// Git forge connections (#2778) — the user's keyring: one read-only
|
||||
// Git forge connections — the user's keyring: one read-only
|
||||
// credential per forge host, used server-side for every forge read on
|
||||
// projects this user owns. The token round-trips masked; the server treats
|
||||
// the mask as "unchanged". Tokens are stored sealed under SECRET_KEY:
|
||||
@@ -1996,12 +1987,12 @@ async function deleteUser(userId: number) {
|
||||
/>
|
||||
<p class="field-hint">How many of the best-scoring rules a reply is checked against (1–10). Only the best can hold the reply; the rest are counted, which is what tells you how close the others came.</p>
|
||||
</div>
|
||||
<!-- THE REVIEW SURFACE (#4102). These numbers are maintained by the
|
||||
<!-- THE REVIEW SURFACE. These numbers are maintained by the
|
||||
model that uses them: it reads which records each bar refused and
|
||||
moves the dial with the argument attached. This panel is the other
|
||||
half of that bargain — a change made on your behalf is one you can
|
||||
read, disagree with, and set back by hand above. -->
|
||||
<!-- STALENESS (#4104). A bar is a cosine similarity, which only means
|
||||
<!-- STALENESS. A bar is a cosine similarity, which only means
|
||||
something inside one embedding model's geometry over documents cut
|
||||
one particular way. Change either and every number above keeps
|
||||
applying while describing nothing. Shown ONLY when something has
|
||||
@@ -2067,10 +2058,7 @@ async function deleteUser(userId: number) {
|
||||
</div>
|
||||
|
||||
<!-- A design system belongs to a PROJECT, and the picker for it lives on
|
||||
the project. There was a setting here that designated the system
|
||||
this install's own interface was built from; it only ever described
|
||||
the app you were already looking at, which is not what the feature
|
||||
is for (#274). -->
|
||||
the project, not here. -->
|
||||
|
||||
<div class="field">
|
||||
<label for="kb-duplicate-threshold-snippet">Near-duplicate report threshold — snippets</label>
|
||||
@@ -3712,7 +3700,7 @@ async function deleteUser(userId: number) {
|
||||
color: var(--fs-text-primary);
|
||||
}
|
||||
/* An absent field reads as absent — never as a blank, and never styled to look
|
||||
like a value it does not have (#3127 checklist 12). */
|
||||
like a value it does not have. */
|
||||
.version-grid dd.version-unknown {
|
||||
font-family: inherit;
|
||||
font-style: italic;
|
||||
@@ -3760,7 +3748,7 @@ async function deleteUser(userId: number) {
|
||||
margin-bottom: 0.35rem;
|
||||
color: var(--fs-text-primary);
|
||||
}
|
||||
/* remainder over .fs-input (components.css, canon #2336; m302) */
|
||||
/* remainder over .fs-input (components.css) */
|
||||
.input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
@@ -4127,7 +4115,7 @@ async function deleteUser(userId: number) {
|
||||
.cell-duration { color: var(--fs-text-tertiary); font-size: 0.8rem; white-space: nowrap; }
|
||||
.text-error { color: var(--fs-error); }
|
||||
/* Bare by design: a `<tr>` has nothing to style
|
||||
that its cells don't carry (#2444). */
|
||||
that its cells don't carry. */
|
||||
.detail-row td { padding: 0 0.75rem 0.75rem; border-bottom: 1px solid var(--fs-border-color); }
|
||||
.detail-ip { font-family: monospace; font-size: 0.8rem; color: var(--fs-text-tertiary); margin-bottom: 0.4rem; }
|
||||
.detail-json {
|
||||
@@ -4495,7 +4483,7 @@ async function deleteUser(userId: number) {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ── Shared areas (milestone 307) ──────────────────────────────────
|
||||
/* ── Shared areas ──────────────────────────────────────────────────
|
||||
The slug is shown deliberately: it is what decides whether two names are
|
||||
the same area, and an admin renaming an entry needs to see it move. */
|
||||
.area-admin-list { list-style: none; margin: 1rem 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--fs-space-2); }
|
||||
@@ -4527,7 +4515,7 @@ async function deleteUser(userId: number) {
|
||||
.area-admin-markers { font-family: var(--fs-font-mono); font-size: 0.82rem; }
|
||||
.area-admin-desc .area-admin-slug { margin-right: 0.25rem; }
|
||||
|
||||
/* The retrieval tuning trail (#4102). Reads as a record, not a control panel:
|
||||
/* The retrieval tuning trail. Reads as a record, not a control panel:
|
||||
the operator is reviewing what was done, and the reason is the part worth
|
||||
reading, so it gets the full-width line under a compact header row. */
|
||||
.tuning-history {
|
||||
@@ -4581,7 +4569,7 @@ async function deleteUser(userId: number) {
|
||||
color: var(--fs-accent);
|
||||
border-color: var(--fs-accent);
|
||||
}
|
||||
/* A shipped default that moved between releases (#4225). Muted rather than
|
||||
/* A shipped default that moved between releases. Muted rather than
|
||||
accented: it is the answer to "did I do this?" being NO for both of the
|
||||
other two, and it wants to be legible without competing with the changes
|
||||
somebody actually made. */
|
||||
@@ -4595,7 +4583,7 @@ async function deleteUser(userId: number) {
|
||||
font-size: 0.85rem;
|
||||
color: var(--fs-text-secondary);
|
||||
}
|
||||
/* Stale calibration (#4104). Tinted rather than loud: the numbers still work,
|
||||
/* Stale calibration. Tinted rather than loud: the numbers still work,
|
||||
they have merely stopped being measurements — that is a "come back to this",
|
||||
not an outage. It only renders when something is actually stale, which is
|
||||
what earns it the tint at all. */
|
||||
|
||||
@@ -42,7 +42,7 @@ function toggleLocationFilter() {
|
||||
if (!showLocationFilter.value && locationActive.value) clearLocation();
|
||||
}
|
||||
|
||||
// Drift check (#2086) — "attention" is everything whose recorded location or
|
||||
// Drift check — "attention" is everything whose recorded location or
|
||||
// code no longer checks out, plus everything whose verdict expired because the
|
||||
// snippet was edited since it was checked.
|
||||
const needsAttentionOnly = ref(false);
|
||||
@@ -58,7 +58,7 @@ const showMergeModal = ref(false);
|
||||
const canonicalId = ref<number | null>(null);
|
||||
const merging = ref(false);
|
||||
|
||||
// Near-duplicate report (#2088). Loaded on demand, not with the list: it's a
|
||||
// Near-duplicate report. Loaded on demand, not with the list: it's a
|
||||
// pairwise scan and most visits to this page aren't a tidy-up.
|
||||
const duplicateGroups = ref<DuplicateGroup[]>([]);
|
||||
const dupLoading = ref(false);
|
||||
|
||||
Reference in New Issue
Block a user