SOC 職業分類に基づく
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/CodySwannGT/lisa --skill harper-cachingコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
SKILL.md を表示中
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.
| 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.