CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 1m6s
CI & Build / Python tests (push) Successful in 1m50s
CI & Build / Build & push image (push) Successful in 38s
Milestone 385 step 8 (#3735 "a lesson is recalled on a project it was not
written on") is defined by opened-on-another-project. The reader's project has
been recorded on every usage event since 0c8e109, but nothing compared it with
the record's own, so the criterion was still unreadable.
- note_usage.usage_for_notes: a second aggregate in the same session joins
notes and counts surfaced_away_count / pulled_away_count. Counted only where
both projects are known and differ; ranked surfacings only (#2477). The
first aggregate is untouched, so events on deleted notes still count.
- empty_usage carries both keys zero-filled; every door that attaches usage
(list_lessons, get_lesson, snippets, knowledge) gets them through
attach_usage.
- UsageBadge tooltip says "On other projects: surfaced N×, opened M×" when it
happened, and nothing when it did not.
- Tests: the mocked split test feeds both aggregates; a unit test for the
away counters; a real-Postgres test that home, unreported and ambient
events are all left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
69 lines
2.6 KiB
Vue
69 lines
2.6 KiB
Vue
<script setup lang="ts">
|
||
/**
|
||
* "N/M used" on a list row — surfaced vs opened, for any record kind.
|
||
*
|
||
* Extracted from SnippetListView when the rule list needed the same chip
|
||
* (milestone 333 step 5). The counts read identically for both; what differs
|
||
* is the ADVICE, which is why that is a prop. A snippet surfaced repeatedly
|
||
* and never opened should probably go; a rule in the same position may simply
|
||
* have a `when_to_apply` that fires on the wrong thing, and telling an
|
||
* operator to delete it would be the wrong nudge half the time.
|
||
*/
|
||
import type { RecordUsage } from "@/types/usage";
|
||
|
||
const props = defineProps<{
|
||
usage?: RecordUsage | null;
|
||
/** What to suggest when this record looks like dead weight. Appended to the
|
||
* tooltip; kind-specific, because the remedies are. */
|
||
deadWeightAdvice: string;
|
||
/** What the record is called in the tooltip's own sentence. */
|
||
noun?: string;
|
||
}>();
|
||
|
||
/** Offered repeatedly and never opened. Three rather than one because one or
|
||
* two surfacings is noise — the record may simply not have come up in a
|
||
* relevant context yet. */
|
||
const isDeadWeight = () =>
|
||
!!props.usage && props.usage.pull_count === 0 && props.usage.surfaced_count >= 3;
|
||
|
||
/** "" renders nothing. A record nobody has surfaced yet gets no badge at all:
|
||
* "0/0" would read as a verdict when it is an absence of evidence — and on a
|
||
* freshly-migrated install that is every row. */
|
||
const label = () => {
|
||
const u = props.usage;
|
||
if (!u || u.surfaced_count === 0) return "";
|
||
return `${u.pull_count}/${u.surfaced_count} used`;
|
||
};
|
||
|
||
const title = () => {
|
||
const u = props.usage;
|
||
if (!u) return "";
|
||
const last = u.last_pulled_at
|
||
? `Last opened ${new Date(u.last_pulled_at).toLocaleDateString()}.`
|
||
: "Never opened.";
|
||
const verdict = isDeadWeight() ? ` ${props.deadWeightAdvice}` : "";
|
||
// Said only when it happened: "0× elsewhere" on every row would read as a
|
||
// finding about records that simply have not been near another project.
|
||
const awaySurfaced = u.surfaced_away_count ?? 0;
|
||
const away = awaySurfaced
|
||
? ` On other projects: surfaced ${awaySurfaced}×, opened ${u.pulled_away_count ?? 0}×.`
|
||
: "";
|
||
return (
|
||
`Surfaced to an agent ${u.surfaced_count}×, opened in full ` +
|
||
`${u.pull_count}×.${away} ${last}${verdict}`
|
||
);
|
||
};
|
||
</script>
|
||
|
||
<template>
|
||
<span
|
||
v-if="label()"
|
||
class="usage-tag"
|
||
:class="{ 'usage-dead': isDeadWeight() }"
|
||
:title="title()"
|
||
>{{ label() }}</span>
|
||
</template>
|
||
|
||
<!-- The look lives in components.css (canon). Nothing scoped here on purpose:
|
||
a view that needs different spacing keeps that as its own remainder. -->
|