From d4c98048e0927ac634b72193fcc0b4c050ef9f21 Mon Sep 17 00:00:00 2001 From: Ether Date: Tue, 28 Apr 2026 11:11:42 +0900 Subject: [PATCH] chore: set up knowledge distillery infrastructure - Add .github/workflows for mark-evidence, batch-refine, curate-report, and apply-changeset (Knowledge Distillery pipeline stages). - Add .knowledge/vault.db as the initial empty knowledge vault, plus empty changesets/ and reports/ directories for the refinement pipeline. - Add CLAUDE.md directives for the Knowledge Vault query workflow and the Memento per-commit session-summary requirement. - Update .gitignore: ignore .knowledge/tmp/, tmp/ (vault-refs scratch), and .mcp.json (runtime secrets); commit vault.db, changesets, reports. --- .github/workflows/apply-changeset.yml | 106 ++++++++++++++++++++++++++ .github/workflows/batch-refine.yml | 98 ++++++++++++++++++++++++ .github/workflows/curate-report.yml | 94 +++++++++++++++++++++++ .github/workflows/mark-evidence.yml | 69 +++++++++++++++++ .gitignore | 10 +++ .knowledge/vault.db | Bin 0 -> 94208 bytes CLAUDE.md | 16 ++++ 7 files changed, 393 insertions(+) create mode 100644 .github/workflows/apply-changeset.yml create mode 100644 .github/workflows/batch-refine.yml create mode 100644 .github/workflows/curate-report.yml create mode 100644 .github/workflows/mark-evidence.yml create mode 100644 .knowledge/vault.db create mode 100644 CLAUDE.md diff --git a/.github/workflows/apply-changeset.yml b/.github/workflows/apply-changeset.yml new file mode 100644 index 0000000..a088a6c --- /dev/null +++ b/.github/workflows/apply-changeset.yml @@ -0,0 +1,106 @@ +name: Knowledge Distillery — Apply Changeset + +on: + pull_request: + types: [closed] + branches: [main, master] + +jobs: + apply-changeset: + if: >- + github.event.pull_request.merged && + startsWith(github.event.pull_request.head.ref, 'knowledge/batch-') + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.base.ref }} + fetch-depth: 0 + + - name: Checkout Knowledge Distillery plugin + uses: actions/checkout@v6 + with: + repository: ether-moon/knowledge-distillery + ref: main + path: .knowledge-distillery-plugin + + - name: Configure git identity + run: | + git config user.name "knowledge-distillery[bot]" + git config user.email "knowledge-distillery[bot]@users.noreply.github.com" + + - name: Extract batch date from branch name + id: batch + env: + BRANCH: ${{ github.event.pull_request.head.ref }} + run: | + if [[ ! "$BRANCH" =~ ^knowledge/batch-([0-9]{4}-[0-9]{2}-[0-9]{2})(-[0-9]+)?$ ]]; then + echo "::error::Unexpected branch format: ${BRANCH}" + exit 1 + fi + DATE="${BASH_REMATCH[1]}" + echo "date=${DATE}" >> "$GITHUB_OUTPUT" + echo "Batch date: ${DATE}" + + - name: Find and apply changeset + run: | + GATE=$(find .knowledge-distillery-plugin -name knowledge-gate -path '*/scripts/*' -type f | head -1) + if [ -z "$GATE" ]; then + echo "::error::knowledge-gate script not found in plugin checkout" + exit 1 + fi + chmod +x "$GATE" + + CHANGESET=".knowledge/changesets/batch-${{ steps.batch.outputs.date }}.json" + + if [ ! -f "$CHANGESET" ]; then + echo "::warning::No changeset file found at ${CHANGESET} — skipping" + exit 0 + fi + + ACCEPTED=$(jq '[.entries[] | select(.status == "accepted")] | length' "$CHANGESET") + echo "Accepted entries to apply: ${ACCEPTED}" + + if [ "$ACCEPTED" -eq 0 ]; then + echo "No accepted entries — skipping vault update" + exit 0 + fi + + "$GATE" _changeset-apply "$CHANGESET" + + - name: Commit and push vault.db + run: | + if git diff --quiet .knowledge/vault.db 2>/dev/null; then + echo "No vault.db changes — skipping commit" + exit 0 + fi + + git add .knowledge/vault.db + git commit -m "knowledge: apply batch ${{ steps.batch.outputs.date }} changeset" + git push origin ${{ github.event.pull_request.base.ref }} + + - name: Clean up batch artifacts + env: + BATCH_DATE: ${{ steps.batch.outputs.date }} + run: | + CHANGESET=".knowledge/changesets/batch-${BATCH_DATE}.json" + REPORT=".knowledge/reports/batch-${BATCH_DATE}.md" + CHANGED=false + + if [ -f "$CHANGESET" ]; then + git rm "$CHANGESET" + CHANGED=true + fi + + if [ -f "$REPORT" ]; then + git rm "$REPORT" + CHANGED=true + fi + + if [ "$CHANGED" = true ]; then + git pull --rebase origin ${{ github.event.pull_request.base.ref }} + git commit -m "knowledge: clean up batch ${BATCH_DATE} artifacts" + git push origin ${{ github.event.pull_request.base.ref }} + fi diff --git a/.github/workflows/batch-refine.yml b/.github/workflows/batch-refine.yml new file mode 100644 index 0000000..7c792d3 --- /dev/null +++ b/.github/workflows/batch-refine.yml @@ -0,0 +1,98 @@ +name: Knowledge Distillery — Batch Refine + +on: + schedule: + - cron: '0 0 * * 1' # Every Monday 09:00 KST (00:00 UTC) + workflow_dispatch: + +jobs: + collect-and-refine: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + issues: write + id-token: write + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Checkout Knowledge Distillery plugin + uses: actions/checkout@v6 + with: + repository: ether-moon/knowledge-distillery + ref: main + path: .knowledge-distillery-plugin + + - name: Write dynamic MCP config + run: | + if [ -n "${{ secrets.LINEAR_API_KEY }}" ]; then echo "::add-mask::${{ secrets.LINEAR_API_KEY }}"; fi + if [ -n "${{ secrets.SLACK_API_KEY }}" ]; then echo "::add-mask::${{ secrets.SLACK_API_KEY }}"; fi + if [ -n "${{ secrets.NOTION_API_KEY }}" ]; then echo "::add-mask::${{ secrets.NOTION_API_KEY }}"; fi + cat > .mcp.json << 'MCPEOF' + { + "mcpServers": { + "github": { + "type": "http", + "url": "https://api.githubcopilot.com/mcp/", + "headers": { + "Authorization": "Bearer ${{ secrets.GITHUB_TOKEN }}", + "X-MCP-Toolsets": "pull_requests,issues,labels" + } + }, + "linear": { + "type": "stdio", + "command": "npx", + "args": ["-y", "mcp-linear"], + "env": { + "LINEAR_API_KEY": "${{ secrets.LINEAR_API_KEY }}" + } + }, + "slack": { + "type": "stdio", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-slack"], + "env": { + "SLACK_BOT_TOKEN": "${{ secrets.SLACK_API_KEY }}" + } + }, + "notion": { + "type": "stdio", + "command": "npx", + "args": ["-y", "@notionhq/notion-mcp-server"], + "env": { + "OPENAPI_MCP_HEADERS": "{\"Authorization\": \"Bearer ${{ secrets.NOTION_API_KEY }}\", \"Notion-Version\": \"2022-06-28\"}" + } + } + } + } + MCPEOF + + - name: Configure git identity + run: | + git config user.name "${{ github.actor }}" + git config user.email "${{ github.actor }}@users.noreply.github.com" + + - uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + prompt: | + Use skill /knowledge-distillery:batch-refine. + Find all PRs with 'knowledge:pending' label, + collect evidence using each PR's Evidence Bundle Manifest, run refinement pipeline, + write accepted entries to a changeset file. + Naming conventions (MUST follow exactly): + - Report branch: knowledge/batch-YYYY-MM-DD (e.g. knowledge/batch-2026-03-27) + - Changeset file: .knowledge/changesets/batch-YYYY-MM-DD.json + - Only entries with .entries[].status == "accepted" are included + On success: update label to 'knowledge:collected'. + On insufficient evidence: leave label as 'knowledge:pending' and report the reason. + Create a Report PR with change summary. + Do NOT modify vault.db directly — the changeset will be applied on merge. + claude_args: "--plugin-dir .knowledge-distillery-plugin --allowedTools 'mcp__github__*,mcp__linear__*,mcp__slack__*,mcp__notion__*,Bash(*),Read(*),Write(*),Glob(*),Grep(*),Skill(*),Agent(*)'" + show_full_output: true + + - name: Cleanup sensitive files + if: always() + run: rm -f .mcp.json diff --git a/.github/workflows/curate-report.yml b/.github/workflows/curate-report.yml new file mode 100644 index 0000000..2c3462b --- /dev/null +++ b/.github/workflows/curate-report.yml @@ -0,0 +1,94 @@ +name: Knowledge Distillery — Curate Report + +on: + issue_comment: + types: [created] + +jobs: + curate-report: + if: >- + github.event.issue.pull_request && + contains(github.event.comment.body, '/curate') && + contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + issues: write + id-token: write + steps: + - name: Get PR details + id: pr + uses: actions/github-script@v7 + with: + script: | + const pr = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number + }); + const branch = pr.data.head.ref; + if (!branch.startsWith('knowledge/batch-')) { + core.notice('Skipping: not a Report PR branch (' + branch + ')'); + core.setOutput('skip', 'true'); + return; + } + core.setOutput('skip', 'false'); + core.setOutput('branch', branch); + core.setOutput('pr_number', context.issue.number); + + - uses: actions/checkout@v6 + if: steps.pr.outputs.skip != 'true' + with: + ref: ${{ steps.pr.outputs.branch }} + fetch-depth: 0 + + - name: Checkout Knowledge Distillery plugin + if: steps.pr.outputs.skip != 'true' + uses: actions/checkout@v6 + with: + repository: ether-moon/knowledge-distillery + ref: main + path: .knowledge-distillery-plugin + + - name: Write dynamic MCP config + if: steps.pr.outputs.skip != 'true' + run: | + cat > .mcp.json << 'MCPEOF' + { + "mcpServers": { + "github": { + "type": "http", + "url": "https://api.githubcopilot.com/mcp/", + "headers": { + "Authorization": "Bearer ${{ secrets.GITHUB_TOKEN }}", + "X-MCP-Toolsets": "pull_requests,issues" + } + } + } + } + MCPEOF + + - name: Configure git identity + if: steps.pr.outputs.skip != 'true' + run: | + git config user.name "${{ github.actor }}" + git config user.email "${{ github.actor }}@users.noreply.github.com" + + - uses: anthropics/claude-code-action@v1 + if: steps.pr.outputs.skip != 'true' + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + prompt: | + Use skill /knowledge-distillery:curate-report. + Process reviewer feedback on Report PR #${{ steps.pr.outputs.pr_number }} + (branch: ${{ steps.pr.outputs.branch }}). + Read all PR comments, classify feedback into reject/update/keep actions, + update the changeset file (.knowledge/changesets/), regenerate the batch report, commit, and post summary. + Do NOT modify vault.db directly — operate on the changeset file only. + claude_args: "--plugin-dir .knowledge-distillery-plugin --allowedTools 'mcp__github__*,Bash(*),Read(*),Write(*),Glob(*),Grep(*),Skill(*),Agent(*)'" + show_full_output: true + + - name: Cleanup sensitive files + if: always() && steps.pr.outputs.skip != 'true' + run: rm -f .mcp.json diff --git a/.github/workflows/mark-evidence.yml b/.github/workflows/mark-evidence.yml new file mode 100644 index 0000000..9c2b3c0 --- /dev/null +++ b/.github/workflows/mark-evidence.yml @@ -0,0 +1,69 @@ +name: Knowledge Distillery — Mark Evidence + +on: + pull_request: + types: [closed] + branches: [main, master] + +jobs: + mark-evidence: + if: >- + github.event.pull_request.merged == true && + !startsWith(github.event.pull_request.head.ref, 'knowledge/batch-') + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + issues: write + id-token: write + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Checkout Knowledge Distillery plugin + uses: actions/checkout@v6 + with: + repository: ether-moon/knowledge-distillery + ref: main + path: .knowledge-distillery-plugin + + - name: Write dynamic MCP config + run: | + if [ -n "${{ secrets.LINEAR_API_KEY }}" ]; then echo "::add-mask::${{ secrets.LINEAR_API_KEY }}"; fi + cat > .mcp.json << 'MCPEOF' + { + "mcpServers": { + "github": { + "type": "http", + "url": "https://api.githubcopilot.com/mcp/", + "headers": { + "Authorization": "Bearer ${{ secrets.GITHUB_TOKEN }}", + "X-MCP-Toolsets": "pull_requests,issues,labels" + } + }, + "linear": { + "type": "stdio", + "command": "npx", + "args": ["-y", "mcp-linear"], + "env": { + "LINEAR_API_KEY": "${{ secrets.LINEAR_API_KEY }}" + } + } + } + } + MCPEOF + + - uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + prompt: | + Use skill /knowledge-distillery:mark-evidence for PR #${{ github.event.pull_request.number }}. + Extract evidence identifiers, write Evidence Bundle Manifest as PR comment, + and add 'knowledge:pending' label. + claude_args: "--plugin-dir .knowledge-distillery-plugin --allowedTools 'mcp__github__*,mcp__linear__*,Bash(*),Read(*),Glob(*),Grep(*),Skill(*),Agent(*)'" + show_full_output: true + + - name: Cleanup sensitive files + if: always() + run: rm -f .mcp.json diff --git a/.gitignore b/.gitignore index 19d6007..5b3acff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,13 @@ .DS_Store node_modules/ .claude/ + +# Knowledge Distillery — vault is committed as binary, reports are committed +# Only ignore temporary/working files +.knowledge/tmp/ + +# Vault usage tracking (consumed and cleared by memento-commit) +tmp/ + +# Dynamic MCP config — contains secrets at runtime, must never be committed +.mcp.json diff --git a/.knowledge/vault.db b/.knowledge/vault.db new file mode 100644 index 0000000000000000000000000000000000000000..208a58cd41a888c9f5d719d39c8b488ebf65d4c6 GIT binary patch literal 94208 zcmeI(L2nz!8GvD~C37W8G95d~78Tr1kg6nMIjL$CK@+$sEv+5G3YAb)90Lsqnq12p zQ>09;?AVura$po_d+T3ledq3x-M0y*~HOAiHd>7mEY?9S3Nq~yd!4K&0{Smx}` z%+Ag;GfPSHY5T*?Fbc%2UVpC{iHn)j8OK@tsSue=CNIA{`IWxT$i!@VAm1JRWt^Hw z<}+{n`rjv2!Tgsh^X24iq8uj>KmY**5I_I{1Q0*~0R#|0;D0J`X5PHqk~+x$%REII z0R#|0009ILKmY**5I_I{1g1T<|F=CjLI42-5I_I{1Q0*~ z0R#|0U>XJ3|DVQDrB4VTfB*srAbT`OGWW&F-@Bil^k#oM z`<K8#Qlsg5X}*4!W(;dv3kv zm+QW$moIPnBFz=WoDe}b>OW|NZBh3>u8ZoH{My;v6gB^fU-PT${&rj<35MEoGG_Cc-kez|r-T=Q>;V%o9Q z_>NbFDw-?h|Cw2Pu8|nlUDqoVoWtEjHs)&g_L^aLkTjtW=4@lL+xcz(mt7DBd>u`Sf)=Ae} zS#b{6ViA24`_1U~z)WW?4>Kp`NKTB;qOaFE*;W+vyY}o*bJLmtNein&mzIP7RyONh zTRE=N<}EhX%p>>kdo!+g{=D<(=Vq5vcU?3+oIR@JX{Ocp(JC_TTXmxy3|jqAu9Dsn zeH=v1=wNWv^4RdNly^4kqR?zb;k}?BuVlWG6{@SpYgCt$tFImG_k&h53fg)Fw)%lO zYBZzqAxc{*wq=1R+zX0w?cSHwa-~bNS?|O1U*VfdjEK_{Cm&ZG*IQn8ekJcwJ;dY^ zlFuFecEVECCu2?d?6O|*b7@R;^w82JpZ)B*C1KPvso9Of3wru!7gJwd%f3)*bIb^+ z3eOsaX1{e?4qQ40Q5bcSN3E(j)|Q$ltxhxCOY)8`aI@EbVC36(tFF-OsD;#2(`9sp z_{zQRKz@YuWsXc8(R6yN@dS}ol|y{Nqzm$y)Evm!J34R9C)Lu?ZWD7#UGiXG&9r1H zK4(?qL+Q0kW8*2dYF76v+oMmv_z9ZSmF){r)T6F0kt{7yu5Jj^K-E^Co5uOnKxdU? zgA-Rv2i&X|_tdzXIM4jhTi+^g`rGS%QQv@WNKRNeNs~`Q*^pe17fxio^73)(QLmxo z@h%tIjQm`j8~<$N@Bha|I7R>g1Q0*~0R#|0009ILKw$a=)ZhQ-v%ksY|9mRD^p~ZJ zi+@~vd*M$D)!gIxCv%_7&APvEJ(#Z$2cSlXj%>qMA)s|<@PBdp1OB<4BwP@+^>dWr(Ys+(=eRY2~x1vED7NBlSre#b(?|#^p0Rn2HO^&bHPFodf)2t+~@KJelM}|KX<>qK7h*Z2n zuhV`*S5Zk_O(k_zm5i&u^Nc(AT8(yt`)N}ushd(s-IPk|ru^#0@MmXZ$w%(Eq>KZJ zhvzuSwwB!RH~s%lN!307Ox(T08{^#zzoL7g?w+0w&oNP6#4z<=a=o=RXP7rPxYbl_ zw0hlJ;chzrpPqRs9k)1gr!ZNdP`qP?J*Jx?CF0=Hr-&;ky$J>nD|7 zhU&qUKfXbkTPO}>h>KdNL0>M={oQVF)oj$vmK&f}_xDe^-h~TCcE1@lO;5SY+&p#s zeW>i?dwsLhYu(WuKfC05ufIMw?2UKaXic~CQKeSjDQ~8MS;?MCeCaQf}S%gF&zJH1;Ds90lOSLu&-( z*taIq$dic(id^Z5I_I{1Q0*~0R#|0 zU^)f(`F}dcm0lr$00IagfB*srAb#2 literal 0 HcmV?d00001 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..3f0aac6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,16 @@ +## Knowledge Vault +- A UserPromptSubmit hook reminds you to query the vault when active entries exist +- When the hook fires and the task involves code modifications, query before planning: + - Single file: `knowledge-gate query-paths ` (summary index by default) + - Multiple files: `knowledge-gate domain-resolve-path ` → `knowledge-gate query-domain ` (summary index by default) + - Topic search: `knowledge-gate search ` (summary index by default) + - Fetch full details only for the specific entries you need: `knowledge-gate get ` or `knowledge-gate get-many ` +- MUST/MUST-NOT rules from returned entries must be strictly followed +- For structural changes in areas without related rules, confirm with a human first +- Do not directly read files in the .knowledge/ directory + +## Memento +- After every git commit, attach a memento session summary as a git note on `refs/notes/commits` +- The summary follows the 7-section format: Decisions Made, Problems Encountered, Constraints Identified, Open Questions, Context, Recorded Decisions, Vault Entries Referenced +- See `/knowledge-distillery:memento-commit` for the full workflow and format specification +- If the PostToolUse hook fires a reminder, follow it — generate the summary and attach the note