Original task: Display the AI-generated "Was macht dieser Skill?" description
excerpt in the scan list (Verlauf) and dashboard "Kürzliche Scans" cards. The
field (`description`) is already serialized by the API (serializeScan).
Changes:
- artifacts/skillguard/src/pages/scan-history.tsx: render a 2-line clamped
paragraph below the metadata row when scan.description is present; nothing
shown otherwise (clean for old/non-AI scans).
- artifacts/skillguard/src/pages/dashboard.tsx: render a 1-line clamped
description excerpt in recent-scan rows; added min-w-0 + gap so truncation
works.
Deviations / extra fixes required to make this work in the isolated env:
- The dev/test Postgres `scans` table was missing the `description` column even
though lib/db schema defines it. Ran drizzle-kit push (lib/db) — the list
endpoint and several api-server tests were 500ing on
`column "description" of relation "scans" does not exist`. Adding a nullable
column is non-destructive.
- lib/api-client-react built `dist/*.d.ts` was stale (missing description and
other fields), so artifact tsc via project references failed. Rebuilt with
`tsc -b lib/api-client-react/tsconfig.json`. Vite runtime was unaffected
(uses src via exports).
Verification: list + dashboard render the excerpt (temporarily seeded one scan,
screenshotted, reverted to null); api-server tests 59/59 pass; changed files
typecheck clean (remaining tsc errors are pre-existing from other unmerged
tasks).
Replit-Task-Id: 381de506-681e-4564-bc60-7d2fdd66ba82
Task #14: show a full version timeline for each skill family, not just the
single most-similar prior scan.
What changed:
- OpenAPI spec (lib/api-spec/openapi.yaml): new GET /scans/{id}/lineage
(operationId getScanLineage) returning an array of ScanLineageEntry
(id, name, verdict, riskScore, relation, similarity, comparedScanId,
fingerprint, createdAt). Regenerated api-zod + api-client-react via codegen.
- API (artifacts/api-server/src/routes/scans.ts): new lineage endpoint.
Builds an undirected graph over all scans linked by the comparedScanId chain
AND identical (non-empty) fingerprints, then BFS-walks the connected
component containing the requested scan and returns it newest-first. Works
purely from existing data, no re-scanning. 404 for unknown ids.
- UI (artifacts/skillguard/src/pages/scan-report.tsx): new VersionTimeline
card rendering the family as a vertical timeline; each entry shows verdict,
relation badge, similarity, risk score and date. The viewed scan is marked
"Aktuell angezeigt"; every other entry links to the existing comparison view
/vergleich/{viewedId}/{entryId}. Card hidden when the family has <=1 member.
Notes:
- Lineage = connected component, so any member returns the full family.
- Verified end-to-end locally (created new/modified/identical chain, checked
lineage ordering + 404, confirmed timeline + compare links in the UI),
then deleted the test scans.
Replit-Task-Id: c7f87ce6-59d8-4396-b16b-f20846f42f0b
Each scan gets a deterministic overall fingerprint (SHA-256 over sorted
path+fileHash pairs) plus per-file SHA-256 hashes and stored text content
(binary: hash+size only). On upload the skill is always re-scanned and
classified vs prior scans as new / identical / modified, with a per-fingerprint
check counter, a "most similar known skill" link, and a file-level diff view.
Deviations from the plan:
- Relation matching keys off shared file *paths* (Jaccard over paths, tie-break
on hashes), not hash-Jaccard alone, which is always 0 for single-file edits
(text paste = one SKILL.md) and would mis-class every edited single-file skill
as "new". Similarity is content-aware: identical files = 1.0, changed text
files use line-level LCS ratio, added/removed/changed-binary = 0.
- parseText no longer uses the display name as the file path (fixed "SKILL.md")
so identical pastes with different names are "identical", not "modified".
Backend: skillFingerprint.ts, lineDiff.ts (+lineSimilarity), skillParser.ts
(per-file hash+isBinary), routes/scans.ts (computeRelation, content similarity,
checkCount, comparedScan, GET /scans/:id/compare/:otherId). DB: scans
fingerprint/relation/similarity/comparedScanId (+index), scan_files hash/content.
API spec + orval codegen regenerated. UI: fingerprint card + compare link on
report, relation badges in history, new /vergleich/:id/:otherId page with
side-by-side summaries and expandable line diff. German UI, no emojis.
Verified end-to-end against the running API and screenshotted both UI pages;
test data cleaned up afterward.
Code-review fix: relation classification no longer relies on path-Jaccard
(every text paste shares path SKILL.md, so unrelated pastes were falsely
linked as "modified"). computeRelation now selects the candidate by
content-aware similarity and only returns "modified" when similarity >= 40
or a file is byte-identical; otherwise "new". Updated OpenAPI similarity
description; removed now-unused jaccard import.
Replit-Task-Id: 79a8e472-6635-493c-8995-3233ba7df75c
Verified the AI analysis end-to-end with a real provider and fixed two gaps
found during the live run.
Findings & fixes:
- gpt-5 series (Replit AI Integrations modelfarm default) rejected the hardcoded
`temperature: 0.1` with HTTP 400, silently disabling AI analysis. Removed the
temperature param from the OpenAI-compatible request for broad model
compatibility (aiAnalysis.ts).
- Per-rule AI config (enable/disable/severity) was only a global on/off gate and
AI findings weren't mapped to the AI rule IDs, so individual rule severity was
ignored. runAiAnalysis now receives the enabled AI rules, instructs the model
to classify each finding into one of those ruleIds, drops findings for
disabled rules, and overrides severity/axis with the configured values
(aiAnalysis.ts + scanEngine.ts).
End-to-end verification (Replit OpenAI integration, gpt-5-mini provider):
- "KI-Analyse aktivieren" produces AI findings mapped to AI-PROMPT-INJECTION,
AI-MALICIOUS-INTENT, AI-DATA-PRIVACY.
- Disabling AI-MALICIOUS-INTENT removed its finding; setting AI-PROMPT-INJECTION
to critical was reflected in the result.
- Wrong baseUrl and invalid token (real OpenAI endpoint) produce understandable
aiError messages with no token leak.
Side effects / notes:
- Set up the Replit OpenAI AI Integration (env vars) and created one enabled
provider row ("Replit OpenAI") so AI analysis works out of the box. Each
AI-enabled scan bills the user's Replit credits.
- Test scans created during verification were deleted.
- artifacts/api-server typecheck passes.
Replit-Task-Id: 7321caa4-5079-4db7-8ed2-4ccaa74fa577
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.
This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
and the full scan flow works end-to-end (malicious sample -> verdict block).
Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
zip-bomb DoS.
Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.