用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/CodySwannGT/lisa --skill harper-caching命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
This skill should be used for any non-trivial request — features, bugs, stories, epics, spikes, or multi-step tasks. It accepts a ticket URL (Jira, Linear, GitHub), a file path containing a spec, or a plain-text prompt. It assembles an agent team, breaks the work into structured tasks, and manages the full lifecycle from research through implementation, code review, deploy, and empirical verification.
any non-trivial request —…
This skill should be used for any non-trivial request — features, bugs, stories, epics, spikes, or multi-step tasks. It accepts a ticket URL (Jira, Linear, GitHub), a file path containing a spec, or a plain-text prompt. It assembles an agent team, breaks the work into structured tasks, and manages the full lifecycle from research through implementation, code review, deploy, and empirical verification.
基于 SOC 职业分类
正在显示 SKILL.md
| name | harper-caching |
| description | implementing Harper… |
Harper can use a local table as a durable cache for an external source. The table stores records, enforces schema/index behavior, exposes normal REST/GraphQL routes, and calls the source Resource only when a record is missing, expired, or explicitly invalidated.
Use a caching table when the cached data should survive process restarts,
participate in Harper queries, or stay coherent across Fabric nodes. Do not keep
API responses in a module-level Map unless the data is deliberately
process-local, disposable, and not part of the application data model.
Cross-check the table declaration in [[harper-schema-graphql]] and the Resource method conventions in [[harper-resources]] before editing. If cached records are queried by filters, sort keys, or relationships, also align indexes and query shape with [[harper-rest-queries]].
Declare a normal @table and expose it with @export when callers should reach
the cache over REST or GraphQL. Use the expiration argument on @table for the
default time-to-live, in seconds:
type WeatherSnapshot @table(expiration: 300) @export(name: "weather") {
locationId: String @primaryKey
city: String @indexed
temperatureF: Float
conditions: String
sourceUpdatedAt: Long
fetchedAt: Long
}
Guidance:
city, tenantId, category, status)
so callers do not fetch broad collections and filter in JavaScript.sourceUpdatedAt, etag, or
fetchedAt when verification, debugging, or conditional revalidation needs it.sourcedFromImplement a source Resource that fetches the upstream record, then attach it to
the table with sourcedFrom. In Lisa template projects, write TypeScript under
src/ and rebuild the generated Harper assets; do not edit resources.js by
hand.
export class WeatherApiSource extends Resource {
static loadAsInstance = false;
async get(target) {
const id = target.id;
const response = await fetch(
`https://api.example.com/weather/${encodeURIComponent(id)}`,
{
headers: { Accept: 'application/json' },
},
);
if (response.status === 404) {
const error = new Error(`Weather location not found: ${id}`);
error.statusCode = 404;
throw error;
}
if (!response.ok) {
const error = new Error(`Weather upstream failed: ${response.status}`);
error.statusCode = 502;
throw error;
}
const data = await response.json();
return {
locationId: id,
city: data.city,
temperatureF: data.,
: data.,
: .(data.),
: .(),
};
}
}
tables..();
When a requested record is missing or stale, Harper calls the source get() and
caches the returned record in the local table. Concurrent requests for the same
missing or stale record share the same upstream load, which avoids a cache
stampede for a single key.
Use expiration for the TTL: after that many seconds, the cached entry is stale
and the next read reloads it from the source. If a project cannot express the
default in schema, sourcedFrom can also receive options:
tables.WeatherSnapshot.sourcedFrom(WeatherApiSource, {
expiration: 300,
eviction: 3600,
scanInterval: 60,
});
Option meanings:
| Option | Use |
|---|---|
expiration | Default TTL in seconds before the cached record is stale. |
eviction | Seconds after expiration before Harper may physically remove the record. |
scanInterval | Seconds between scans for expired records. |
Prefer schema directives for stable application behavior because the data model
and default freshness policy stay together. Use sourcedFrom options when the
cache attachment is intentionally dynamic or the downstream Harper version lacks
the needed schema directive.
When the upstream provides ETag, Last-Modified, or Cache-Control, pass that
freshness model through the source Resource instead of overwriting good cached
data with every revalidation.
export class WeatherApiSource extends Resource {
static loadAsInstance = false;
async get(target) {
const context = this.getContext();
const headers = new Headers({ Accept: 'application/json' });
if (context.replacingVersion) {
headers.set(
'If-Modified-Since',
new Date(context.replacingVersion).toUTCString(),
);
}
const response = await fetch(
`https://api.example.com/weather/${encodeURIComponent(target.id)}`,
{ headers },
);
if (response.status === 304) {
return context.replacingRecord;
}
const maxAge = response.headers
.get('Cache-Control')
?.match(/max-age=(\d+)/)?.[1];
if (maxAge) {
context.expiresAt = Date.now() + (maxAge) * ;
}
context. = response..();
response.();
}
}
Use allowStaleWhileRevalidate(entry, id) on the cached table when stale data is
acceptable during refresh:
export class WeatherSnapshot extends tables.WeatherSnapshot {
static loadAsInstance = false;
allowStaleWhileRevalidate(entry, id) {
return Date.now() - entry.expiresAt < 60_000;
}
}
Return false or omit the method when callers must wait for a fresh value.
Use invalidation when an admin action, webhook, scheduled refresh, or upstream write makes the cached record stale before its TTL.
await tables.WeatherSnapshot.invalidate('nyc');
The next get() for that id reloads from the source. If the project Harper
version or resource shape does not expose invalidate(), use an explicit delete
or overwrite path and document the behavior:
await tables.WeatherSnapshot.delete('nyc');
await tables.WeatherSnapshot.get('nyc');
For write-through caches, implement put, patch, or delete on the source
Resource only when those operations should update the upstream system. Otherwise,
reject writes or keep cache mutation behind an operator-only Resource so clients
do not accidentally diverge from the source of truth.
In a Fabric deployment, treat every node as capable of serving a read while cache state is replicating:
get() deterministic and idempotent. A second node may reload the same
stale key.Prove both cache fill and cache hit behavior against a running Harper app:
bun run build, then the project Harper dev command).curl -sS http://localhost:9926/weather/nyc | jq .
curl -sS http://localhost:9926/weather/nyc | jq .
fetchedAt or equivalent freshness evidence.curl -sS -X DELETE http://localhost:9926/weather/nyc
curl -sS http://localhost:9926/weather/nyc | jq .
expiration in a local-only test fixture,
wait past the TTL, and verify either blocking refresh or stale-while-revalidate
behavior matches the Resource implementation.