Skip to content

feat(intelligence): GitHub 連携スキル推論基盤 Phase1+2(ADR-0016)#408

Merged
yusuke0610 merged 5 commits into
mainfrom
feat/github-skill-inference
Jun 25, 2026
Merged

feat(intelligence): GitHub 連携スキル推論基盤 Phase1+2(ADR-0016)#408
yusuke0610 merged 5 commits into
mainfrom
feat/github-skill-inference

Conversation

@yusuke0610

@yusuke0610 yusuke0610 commented Jun 25, 2026

Copy link
Copy Markdown
Owner

概要

ADR-0016 に基づき、GitHub 連携のスキル推論を 3 層モデル(Skill / Evidence / Proficiency) へ再設計する基盤と、discover(Linguist resolve)+ declare(manifest 宣言) までを実装します。verify / monorepo / private / deps.dev は ADR どおり後続フェーズです。

スコープは事前に合意した Phase1+2(基盤+declare)。既存の JSON キャッシュ経路・旧 skill_taxonomy は残置し、非破壊で 3 層経路を追加しています。

変更点

  • discover(D3・D4): Linguist 内部マスタ skills/data/linguist_master.jsonskills/linguist.py。連携ホットパスで外部を叩かず resolve。エイリアス名寄せ・data 言語の既定除外(keep_data で補正)・表示名補正(HCL→Terraform 等)。
  • declare(D7): plugin 型 ManifestParser + Tier1 パーサ(go.mod / pyproject.toml / requirements.txt / package.json / Cargo.toml)。dependency_kind(direct/dev/indirect/peer/build)を保持。go.mod// indirectdevDependencies を区別。
  • 3 層モデル(D1): github_skills / github_skill_evidence / github_skill_proficiency + migration 0045新規テーブルのみ・既存改変なし)。
  • 配線: aggregator で L1+L2 を組み立て、GitHubSkillRepository.replace_for_user で洗い替え永続化を run_github_link の phase C に接続。collect_repos(collect_manifests=True) で直下 manifest を取得。
  • 読み出し API: GET /api/github-link/skills + schemas。OpenAPI 変更に伴い web/src/api/generated.ts を再生成。
  • ドキュメント: ADR-0016 追加。不要になった ADR-0011(textlint 校正)を Deprecated 化(※本機能撤去に伴う別件のドキュメント更新で、同梱)。

レビュー観点(設計判断)

  • D2「別型」の表現: ストレージは kind 判別カラムの単一テーブル+ドメイン層で型分離(言語=byte 比率 / package=宣言+kind)。言語の ecosystem は一意制約を効かせるため NULL でなく ""(API では null へ正規化)。
  • Linguist マスタは暫定キュレーション: languages.yml の完全取り込みは後続。未収録言語は programming として採用(取りこぼし防止、ノイズは exclude で制御)。
  • L3 は本フェーズ未投入: 再連携の洗い替えで L3 も CASCADE 削除される(埋め始めたら保全が必要)— ADR の Open Question 通り後続課題。

テスト / CI

  • make ci green(backend lint+test / web lint+test / build)。
  • 追加テスト: manifest パーサ各種 / Linguist リゾルバ / aggregator / 永続化+エンドポイント(認可・空・ユーザー分離・冪等)。
  • alembic upgrade head を実 libSQL 経路相当(temp DB)で確認し、0045 までクリーンに適用・3 テーブル作成を確認。
  • codegen 再生成済み(generated.ts +157 行、skill エンドポイントのみ)。

環境変数 / 依存

  • 追加なし(TOML パースは stdlib tomllib)。

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a signed-in API endpoint to view inferred GitHub skills with evidence and optional proficiency context.
    • Introduced a 3-layer GitHub skill model with persistence, plus automatic overwrite/update when re-synced.
    • Expanded repository analysis with manifest/package parsing and improved language normalization.
  • Bug Fixes
    • Ensured skills/evidence are correctly scoped per user and refreshed cleanly on re-sync.
  • Tests
    • Added coverage for aggregation, language resolution, manifest parsing, and the skills endpoint.
  • Documentation
    • Updated ADRs to reflect the new skill inference approach and status changes.

ADR-0016 に基づき、GitHub 連携のスキル推論を 3 層モデル
(Skill / Evidence / Proficiency)へ再設計する基盤と、discover(Linguist
resolve)+ declare(manifest 宣言)までを実装する。verify / monorepo /
private / deps.dev は ADR どおり後続フェーズ。

- discover: Linguist 内部マスタ + リゾルバ(連携ホットパスで外部非依存、
  data 言語の既定除外を keep_data で補正 / D3・D4)
- declare: plugin 型 manifest パーサ(go.mod / pyproject.toml /
  requirements.txt / package.json / Cargo.toml)。dependency_kind を保持(D7)
- 3 層モデル github_skills / github_skill_evidence / github_skill_proficiency
  + migration 0045(新規テーブルのみ・既存改変なし)
- 集計(aggregator)→ 洗い替え永続化を github-link タスクへ配線
- 読み出し API GET /api/github-link/skills + schemas(codegen 再生成)
- ADR-0016 追加 / 不要になった ADR-0011 を Deprecated 化
- テスト: parsers / linguist / aggregator / 永続化 + endpoint(認可・分離・冪等)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@yusuke0610, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 50 minutes and 47 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7abb234b-4575-4ed7-b962-32d283df29df

📥 Commits

Reviewing files that changed from the base of the PR and between 5543f98 and 198b58e.

📒 Files selected for processing (2)
  • backend/app/services/intelligence/skills/aggregator.py
  • backend/tests/test_skill_aggregator.py
📝 Walkthrough

Walkthrough

Adds GitHub skill storage, language normalization, manifest parsing, aggregation, persistence, and a new skills read endpoint. It also updates generated frontend API types, adds tests for the flow, and changes one unrelated ADR status note.

Changes

GitHub skill inference pipeline

Layer / File(s) Summary
Skill tables and models
backend/app/models/skill.py, backend/app/models/__init__.py, backend/alembic_migrations/versions/0045_add_github_skill_tables.py, docs/adr/0016-github-skill-inference.md
Defines the GitHub skill ORM models, matching migration tables and indexes, public exports, and the ADR for the 3-layer model.
Language normalization
backend/app/services/intelligence/skills/linguist.py, backend/tests/test_skill_linguist.py, backend/app/services/intelligence/skills/resources/linguist_master.json
Resolves Linguist language names into canonical skill metadata and adds tests for alias, exclusion, fallback, and empty-input behavior.
Manifest contracts and shared helpers
backend/app/services/intelligence/skills/types.py, backend/app/services/intelligence/skills/manifests/*
Defines the shared package declaration type, parser protocol, parser export surface, and PEP 508 package-name extraction helper.
Manifest parsers and dispatch
backend/app/services/intelligence/skills/manifests/*, backend/tests/test_skill_parsers.py
Implements Go, Python, npm, Cargo, and requirements parsers, adds registry dispatch, and tests filename routing and dependency-kind classification.
Aggregation and collection
backend/app/services/intelligence/skills/__init__.py, backend/app/services/intelligence/skills/aggregator.py, backend/app/services/intelligence/github/api_client.py, backend/app/services/intelligence/github_collector.py, backend/app/services/intelligence/github_link_service.py, backend/tests/test_skill_aggregator.py
Aggregates repository language and manifest signals into detected skills, adds GitHub file-fetch helpers, collects manifests when requested, and wires the GitHub link job to build detected skills.
Persistence and API surface
backend/app/repositories/__init__.py, backend/app/repositories/skill.py, backend/app/services/intelligence/github_link_service.py, backend/app/schemas/github_skill.py, backend/app/routers/github_link.py, web/src/api/generated.ts, backend/tests/test_github_skills_api.py
Persists the detected skills, exposes the skills response schema, adds the read endpoint, and updates generated frontend API types and endpoint tests.

Frontend textlint ADR status update

Layer / File(s) Summary
ADR status change
docs/adr/0011-frontend-textlint-proofread.md
Updates the ADR status and adds the replacement proofreading note.

Sequence Diagram(s)

sequenceDiagram
  participant run_github_link
  participant collect_repos
  participant aggregate_skills
  participant GitHubSkillRepository
  run_github_link->>collect_repos: collect_manifests=True
  collect_repos-->>run_github_link: RepoData list
  run_github_link->>aggregate_skills: RepoSkillInput list
  aggregate_skills-->>run_github_link: detected_skills
  run_github_link->>GitHubSkillRepository: replace_for_user(detected_skills)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hop through skills so neat and bright,
manifests and languages line up right.
Evidence crumbs in little rows,
GitHub skill carrots grow and glow! 🥕
Hoppity-hop, the tables sing tonight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.87% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: GitHub-linked skill inference foundation for Phase 1+2 under ADR-0016.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/github-skill-inference

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (1)
backend/tests/test_github_skills_api.py (1)

87-97: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Idempotency test doesn't cover orphaned evidence cleanup.

test_replace_is_idempotent only asserts surviving canonical_name values. It would not detect orphaned github_skill_evidence rows left behind if the Core-level delete in replace_for_user doesn't cascade (see backend/app/repositories/skill.py Lines 40-42). Consider asserting the evidence row count drops to match the second replace to lock in the cascade behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_github_skills_api.py` around lines 87 - 97, The
idempotency test for GitHub skills only checks surviving canonical_name values
and can miss leftover github_skill_evidence rows after replace_for_user. Update
test_replace_is_idempotent in test_github_skills_api.py to also verify the
evidence count after the second replace matches the remaining Python skill, so
the cascade cleanup behavior in GitHubSkillRepository.replace_for_user is locked
in.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/app/models/skill.py`:
- Around line 79-84: The raw SQL delete in
GitHubSkillRepository.replace_for_user bypasses the ORM cascades defined on
GitHubSkill relationships, so orphaned GitHubSkillEvidence and
GitHubSkillProficiency rows can remain when foreign keys are not enforced. Fix
this by either enabling PRAGMA foreign_keys=ON for every connection in
backend/app/db/database.py (so ON DELETE CASCADE works) or by changing
replace_for_user to delete through the ORM Session instead of
__table__.delete(), ensuring the evidence and proficiency relationships on
GitHubSkill are cleaned up.

In `@backend/app/repositories/skill.py`:
- Around line 40-42: The bulk delete in GitHubSkillRepository (the
self.db.execute(GitHubSkill.__table__.delete()...) path) bypasses ORM cascade,
so make sure SQLite foreign-key enforcement is enabled for the app/test engine
via the engine setup or connection hook. If that cannot be guaranteed, change
the delete flow to load the GitHubSkill entities and remove them with
session.delete so the GitHubSkill.evidence and GitHubSkill.proficiency cascades
always run regardless of backend.

In `@backend/app/services/intelligence/github_link_service.py`:
- Around line 168-171: Skill persistence is committed after the cache is already
marked completed, creating a transient inconsistent state if
GitHubSkillRepository.replace_for_user fails. In github_link_service.py, adjust
the ordering around the db.commit() call and GitHubSkillRepository(db,
user_id).replace_for_user(detected_skills) so the skill replace happens in the
same transaction before finalizing the completed status, or otherwise ensure
both writes are committed atomically. Confirm the intended transactional flow in
the surrounding service method so the cache state and persisted skills stay in
sync.

In `@backend/app/services/intelligence/skills/aggregator.py`:
- Around line 135-159: The package deduplication in `_collect_packages` is using
raw `decl.name`, which can create duplicate `DetectedSkill` entries for PyPI
names that differ only by case or separator style. Update the dedup key logic in
`_collect_packages` so that when `decl.ecosystem` is `pypi`, the package name is
PEP 503-normalized before it is stored in `best` and passed to `_upsert`; keep
other ecosystems unchanged. Use the existing `_collect_packages` and `_upsert`
flow as the place to apply the normalization consistently for canonical
matching.

In `@backend/app/services/intelligence/skills/manifests/_pep508.py`:
- Around line 9-19: extract_package_name currently misclassifies URL/VCS direct
references as package names because _NAME_RE matches the leading scheme; update
the parsing in extract_package_name to detect direct URL/VCS specs first and
return None for them, while keeping normal PEP 508 names working. Use the
existing extract_package_name helper in _pep508.py as the single place to apply
the fix so downstream callers in requirements_txt.py and pyproject.py stop
emitting invalid names.

In `@backend/tests/test_skill_linguist.py`:
- Around line 44-49: The fallback test in
test_unknown_language_falls_back_to_included currently uses Brainfuck, which may
later become a curated language and stop exercising the unknown-language path.
Update the test to use a clearly synthetic token that will never appear in
linguist_master.json, and keep the assertions on resolve_language so it verifies
the full fallback shape through the canonical result.

In `@docs/adr/0011-frontend-textlint-proofread.md`:
- Around line 5-8: The ADR index is now inconsistent because the deprecation
note for ADR-0011 no longer matches the status listed in CONTRIBUTING.md. Update
the ADR index entry for 0011 to reflect the new deprecated/rejected state when
editing this document, and verify the status text in the ADR listing stays
aligned with the deprecated notice.

---

Nitpick comments:
In `@backend/tests/test_github_skills_api.py`:
- Around line 87-97: The idempotency test for GitHub skills only checks
surviving canonical_name values and can miss leftover github_skill_evidence rows
after replace_for_user. Update test_replace_is_idempotent in
test_github_skills_api.py to also verify the evidence count after the second
replace matches the remaining Python skill, so the cascade cleanup behavior in
GitHubSkillRepository.replace_for_user is locked in.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3d4bc6b9-809f-4e3e-9c76-70179aa87bcf

📥 Commits

Reviewing files that changed from the base of the PR and between 7890f46 and 0ebb475.

📒 Files selected for processing (30)
  • backend/alembic_migrations/versions/0045_add_github_skill_tables.py
  • backend/app/models/__init__.py
  • backend/app/models/skill.py
  • backend/app/repositories/__init__.py
  • backend/app/repositories/skill.py
  • backend/app/routers/github_link.py
  • backend/app/schemas/github_skill.py
  • backend/app/services/intelligence/github/api_client.py
  • backend/app/services/intelligence/github_collector.py
  • backend/app/services/intelligence/github_link_service.py
  • backend/app/services/intelligence/skills/__init__.py
  • backend/app/services/intelligence/skills/aggregator.py
  • backend/app/services/intelligence/skills/linguist.py
  • backend/app/services/intelligence/skills/manifests/__init__.py
  • backend/app/services/intelligence/skills/manifests/_pep508.py
  • backend/app/services/intelligence/skills/manifests/base.py
  • backend/app/services/intelligence/skills/manifests/cargo_toml.py
  • backend/app/services/intelligence/skills/manifests/go_mod.py
  • backend/app/services/intelligence/skills/manifests/package_json.py
  • backend/app/services/intelligence/skills/manifests/pyproject.py
  • backend/app/services/intelligence/skills/manifests/registry.py
  • backend/app/services/intelligence/skills/manifests/requirements_txt.py
  • backend/app/services/intelligence/skills/types.py
  • backend/tests/test_github_skills_api.py
  • backend/tests/test_skill_aggregator.py
  • backend/tests/test_skill_linguist.py
  • backend/tests/test_skill_parsers.py
  • docs/adr/0011-frontend-textlint-proofread.md
  • docs/adr/0016-github-skill-inference.md
  • web/src/api/generated.ts

Comment thread backend/app/models/skill.py
Comment thread backend/app/repositories/skill.py Outdated
Comment thread backend/app/services/intelligence/github_link_service.py Outdated
Comment thread backend/app/services/intelligence/skills/aggregator.py
Comment thread backend/app/services/intelligence/skills/manifests/_pep508.py
Comment thread backend/tests/test_skill_linguist.py
Comment thread docs/adr/0011-frontend-textlint-proofread.md
yusuke0610 and others added 4 commits June 25, 2026 12:05
.gitignore の `data/` ルールにより skills/data/linguist_master.json が
コミットされず、CI のクリーンチェックアウトで FileNotFoundError になっていた
(ローカルはディスク上に存在するため test が通っていた)。データファイルを
skills/resources/ へ移して追跡対象にする。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- replace_for_user を ORM セッション削除に変更し、cascade="all, delete-orphan"
  で evidence/proficiency も確実に削除する。Core 一括 DELETE は DB の
  ON DELETE CASCADE に依存し、SQLite/libSQL は PRAGMA foreign_keys=ON でないと
  孤児行を残すため、バックエンド非依存にする(Critical/Major 指摘)。
- github_link_service: 3 層スキルの永続化を cache を completed にする前へ移動。
  永続化失敗時に「completed なのにスキル無し」を避ける(Minor 指摘)。
- idempotency テストを強化し、削除後に孤児 evidence が残らないことを assert。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- _pep508: URL/VCS 直指定(https:// / git+https:// 等)を package 名抽出前に
  弾く。従来は "https"/"git" を誤って package 名として下流に流していた(Major)。
  requirements.txt の回帰テストを追加。
- CONTRIBUTING.md の ADR 索引: 0011 を Deprecated に更新し、0016 を追加(索引が
  本文と不整合だった指摘 / Minor)。
- linguist の未収録フォールバックテストを、将来収録され得る実在名(Brainfuck)から
  合成トークンへ変更し fallback 形状を検証(Minor)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
aggregator._collect_packages の dedup キーが raw な decl.name を使っており、
pypi で大小文字・区切り違い(Flask/flask、ruamel.yaml/ruamel-yaml 等)が
別スキルとして重複していた。pypi のみ PEP 503 正規化(小文字化・-_. を - に畳む)
してから canonical 名・キーに使う。go/npm/cargo は package ID をそのまま維持。
正規化と非正規化の回帰テストを追加。

なお同 PR の models/skill.py の Core DELETE 指摘は前コミットで ORM 削除に修正済み
のため対象外。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant