Files
Kazuki Yamada fa06e5059c fix(website): Address PR review feedback on siteverify metric
Six items from gemini, claude initial review, and claude follow-up:

- turnstile.ts: Update misleading comment that claimed the metric filters
  on `event=turnstile_siteverify` and `outcome=success`. The actual
  Cloud Monitoring metrics in `monitoring/metrics/` filter on
  `siteverifyDurationMs` field presence, which uniformly captures both
  the parallel success log (event=turnstile_siteverify) and the four
  rejectAndLog failure paths (event=pack_completed). The comment
  contradicted README and YAML and would mislead future readers.
- turnstile.ts: Wrap rejectAndLog in a local `rejectWithDuration` helper
  so every post-siteverify branch automatically carries
  `siteverifyDurationMs`. Prevents drift if a fifth reject reason gets
  added later.
- client.ts: Split the wire-protocol `PackProgressStage` (server-emitted
  SSE values) from the display-only `DisplayProgressStage` superset that
  adds `verifying`. Keeping the synthetic stage out of the wire type
  prevents silent divergence with the server's `PackProgressStage`.
- usePackRequest.ts, TryItLoading.vue, TryItResult.vue: Switch the
  display-side type to `DisplayProgressStage`. `onProgress` callbacks
  still take the wire `PackProgressStage`.
- usePackRequest.ts: Clear `progressStage` on token-acquisition failure
  branches (aborted / error). Functionally invisible since loading=false
  hides the loading UI, but prevents the next submit's verifying flash
  from briefly showing the previous run's stale state.
- monitoring/metrics/turnstile_siteverify_duration.yaml: Retune the
  exponential bucket layout for the 100ms-1s SLO band where decisions
  get made. Doubling buckets only placed ~3 boundaries between 100ms
  and 1s; growthFactor=1.5 with scale=10 places ~8 boundaries there.
  18 finite buckets cover 10ms to ~9.85s, comfortably above the 5s
  siteverify timeout so timeouts don't land in overflow.
- monitoring/README.md: Document that pre-network rejections
  (secret_missing, missing_token, token_too_long) intentionally don't
  carry siteverifyDurationMs, so they're excluded from both metrics
  but still appear in the existing pack_requests metric.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 21:42:21 +09:00

127 lines
2.8 KiB
Vue

<script setup lang="ts">
import { computed } from 'vue';
import type { DisplayProgressStage } from '../api/client';
interface Props {
stage?: DisplayProgressStage | null;
message?: string | null;
}
const props = defineProps<Props>();
const stageMessages: Record<DisplayProgressStage, string> = {
verifying: 'Verifying request...',
'cache-check': 'Checking cache...',
cloning: 'Cloning repository...',
'repository-fetch': 'Fetching repository...',
extracting: 'Extracting files...',
processing: 'Processing files...',
};
const MAX_DETAIL_LENGTH = 60;
const detailMessage = computed(() => {
const text = props.message || (props.stage && stageMessages[props.stage]) || '...';
if (text.length <= MAX_DETAIL_LENGTH) return text;
return `${text.slice(0, MAX_DETAIL_LENGTH)}...`;
});
</script>
<template>
<div class="loading">
<div class="loading-header">
<div class="spinner"></div>
<p>Processing repository...</p>
</div>
<p class="loading-detail">{{ detailMessage }}</p>
<div class="sponsor-section">
<p class="sponsor-header">Special thanks to:</p>
<a href="https://go.warp.dev/repomix" target="_blank" rel="noopener noreferrer">
<img alt="Warp sponsorship" width="400" src="https://raw.githubusercontent.com/warpdotdev/brand-assets/main/Github/Sponsor/Warp-Github-LG-01.png">
</a>
<p class="sponsor-title">
<a href="https://go.warp.dev/repomix" target="_blank" rel="noopener noreferrer">
Warp, built for coding with multiple AI agents
</a>
</p>
<p class="sponsor-subtitle">
<a href="https://go.warp.dev/repomix" target="_blank" rel="noopener noreferrer">
Available for MacOS, Linux, & Windows
</a>
</p>
</div>
</div>
</template>
<style scoped>
.loading {
padding: 24px;
text-align: center;
}
.loading-header {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}
.loading-header p {
margin: 0;
}
.loading-detail {
margin: 4px 0 0;
font-size: 0.8em;
color: var(--vp-c-text-3);
}
.spinner {
flex-shrink: 0;
width: 20px;
height: 20px;
border: 2px solid var(--vp-c-brand-1);
border-radius: 50%;
border-top-color: transparent;
animation: spin 1s linear infinite;
}
.sponsor-section {
margin-top: 16px;
display: flex;
flex-direction: column;
align-items: center;
}
.sponsor-section p {
margin: 8px 0;
}
.sponsor-section .sponsor-header {
font-size: 0.9em;
}
.sponsor-section img {
max-width: 100%;
height: auto;
margin: 12px 0;
}
.sponsor-section .sponsor-title {
font-weight: bold;
font-size: 1.1em;
color: var(--vp-c-brand-1);
text-decoration: underline;
}
.sponsor-section .sponsor-subtitle {
font-size: 0.9em;
color: var(--vp-c-brand-1);
text-decoration: underline;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>