Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions .github/ISSUE_TEMPLATE/skill-feedback.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Skill feedback / drift report
description: Report that a morph-doc Skill (or its paired MDX) gave incorrect, stale, or unroutable guidance.
title: "[skill-drift] <skill-id>: <one-line summary>"
labels:
- skill-drift
assignees: []
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to flag a Skill drift. This template powers the
feedback loop for the **Docs-as-SKILL** contract described in
[`VISION.md`](https://github.com/morph-l2/morph-doc/blob/main/VISION.md).
The doc maintainer triages every `skill-drift` issue.

- type: input
id: skill-id
attributes:
label: Skill ID
description: Directory name under `skills/`, e.g. `morph-contracts`, `morph-js-sdk`. Leave blank if you are reporting an MDX page with no paired Skill.
placeholder: morph-contracts
validations:
required: false

- type: input
id: doc-path
attributes:
label: Related doc path
description: Path relative to repo root, e.g. `docs/build-on-morph/developer-resources/1-contracts.md`. Optional if unknown.
placeholder: docs/...
validations:
required: false

- type: dropdown
id: category
attributes:
label: Drift category
description: Pick the best fit; maintainers may relabel during triage.
options:
- Stale fact (address / chainId / RPC / token changed)
- Wrong routing (Skill triggered for unrelated question)
- Missing routing (question should match a Skill but did not)
- Executable snippet no longer runs
- Self-check item failed during real use
- Other
validations:
required: true

- type: textarea
id: what-happened
attributes:
label: What happened
description: Describe the prompt, the Skill that fired (if any), and the answer you received.
placeholder: |
Prompt: "...the user's question..."
Skill fired: morph-contracts
Answer given: "..."
What was wrong: "..."
validations:
required: true

- type: textarea
id: expected
attributes:
label: What you expected instead
placeholder: |
- Expected fact: ...
- Source of truth: ...
validations:
required: true

- type: input
id: source
attributes:
label: Authoritative source (optional)
description: Link or path to the canonical value (chain explorer, source code, JSON registry, etc.).
placeholder: https://... or morph-bridge/public/morph-list/src/mainnet/tokenList.json
validations:
required: false

- type: dropdown
id: agent
attributes:
label: Agent / IDE where this occurred
options:
- Cursor
- Claude Code
- OpenClaw
- Windsurf
- Codex
- Web chat
- Other
- Not applicable
validations:
required: false

- type: textarea
id: additional
attributes:
label: Additional context
description: Version info, chain (Mainnet / Hoodi), package versions, screenshots, etc.
validations:
required: false
145 changes: 145 additions & 0 deletions .github/workflows/skill-freshness-bot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Skill freshness bot (Phase 1) — see scripts/skill-freshness-bot.DESIGN.md
#
# - Does NOT run on pull_request (freshness stays warn-only in npm test).
# - Opens or updates a single tracking Issue on default branch when warnings exist.

name: skill-freshness-bot

on:
schedule:
# Mondays 09:00 UTC
- cron: '0 9 * * 1'
workflow_dispatch:

permissions:
contents: read
issues: write

jobs:
report:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false

Comment thread
coderabbitai[bot] marked this conversation as resolved.
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '20'
cache: npm

- name: Generate freshness report
run: |
mkdir -p .local
node scripts/skill-freshness-report.mjs --json --out .local/skill-freshness-report.json

- name: Upload report artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: skill-freshness-report
path: .local/skill-freshness-report.json
retention-days: 30

- name: Sync tracking issue
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
env:
REPORT_PATH: .local/skill-freshness-report.json
with:
script: |
const fs = require('fs');
const report = JSON.parse(fs.readFileSync(process.env.REPORT_PATH, 'utf8'));
const owner = context.repo.owner;
const repo = context.repo.repo;
const titlePrefix = '[skill-freshness] Weekly re-verify queue';
const botLabel = 'skill-freshness';

const list = await github.rest.issues.listForRepo({
owner,
repo,
state: 'all',
labels: botLabel,
per_page: 100,
});
const existing = list.data.find((i) => i.title.startsWith(titlePrefix));

if (report.warningCount === 0) {
if (existing) {
await github.rest.issues.createComment({
owner,
repo,
issue_number: existing.number,
body: [
'✅ **skill-freshness-bot**: no warnings in the latest scan.',
'',
`- Generated: \`${report.generatedAt}\``,
`- Skills scanned: ${report.skillCount}`,
'',
'Closing this tracking issue. It will reopen on the next scheduled run if warnings return.',
].join('\n'),
});
await github.rest.issues.update({
owner,
repo,
issue_number: existing.number,
state: 'closed',
});
}
core.info('No freshness warnings; tracking issue closed or none needed.');
return;
}

const tableHeader =
'| Skill | Code | last_verified | Age (days) |\n|-------|------|---------------|------------|';
const tableRows = report.warnings.map((w) => {
const lv = w.lastVerified ?? '—';
const age = w.ageDays ?? '—';
return `| \`${w.skillId}\` | ${w.code} | ${lv} | ${age} |`;
});

const body = [
'## Skill freshness report',
'',
'Automated scan from [`skill-freshness-bot`](.github/workflows/skill-freshness-bot.yml).',
'Human-reported factual errors should still use the **skill-drift** issue template.',
'',
`**Threshold:** ${report.thresholdDays} days — see [VISION.md § Skill Verification Metadata](https://github.com/${owner}/${repo}/blob/main/VISION.md#skill-verification-metadata-freshness-contract)`,
'',
tableHeader,
...tableRows,
'',
'### Maintainer checklist',
'',
'- [ ] Re-read each listed Skill against its `verified_against` paths',
'- [ ] Update `last_verified` to today (UTC `YYYY-MM-DD`) in the same PR as any fact fix',
'- [ ] Run `npm test`',
'',
`**Run:** ${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}`,
`**Generated:** \`${report.generatedAt}\``,
`**Warnings:** ${report.warningCount} (${report.staleCount} stale)`,
].join('\n');

const title = `${titlePrefix} (${report.warningCount} warning${report.warningCount === 1 ? '' : 's'})`;

if (existing) {
await github.rest.issues.update({
owner,
repo,
issue_number: existing.number,
title,
body,
state: 'open',
});
core.info(`Updated issue #${existing.number}`);
return;
}

const created = await github.rest.issues.create({
owner,
repo,
title,
body,
labels: [botLabel, 'bot'],
});
core.info(`Created issue #${created.data.number}`);
29 changes: 28 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,32 @@ yarn-error.log*
.anima
data
build
scripts
# scripts
build
examples
.cursor
.claude
.openclaw

# __tests__
# run-tests.mjs
content

# agent
skills-plan
memory
USER.md
SOUL.md
TOOLS.md
IDENTITY.md
HEARTBEAT.md
DREAMS.md
*.zh.md

# dapp agent
planning
research

# skill-creator description optimization (--results-dir); see skills/README.md
.local
vendor/skill-creator
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node __tests__/doc-skill-pairing.test.mjs
node __tests__/morph-doc-skill-inventory.test.mjs
node __tests__/skills-sidebar.test.mjs
npm run build
Loading