Repository navigation
chore(deps-dev): bump eslint-plugin-react-hooks from 7.0.1 to 7.1.1 #2311
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Daemon Gate | |
| on: | |
| workflow_dispatch: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| concurrency: | |
| group: daemon-gate-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| daemon-integration-tests: | |
| name: Daemon Integration Tests | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - uses: pnpm/action-setup@903f9c1a6ebcba6cf41d87230be49611ac97822e # v6 | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 | |
| with: | |
| node-version: '20' | |
| cache: 'pnpm' | |
| - run: pnpm install --frozen-lockfile | |
| # Build the full workspace — core + engine alone is insufficient because | |
| # @holoscript/core/dist/index.js transitively loads @holoscript/core-types, | |
| # @holoscript/uaal, @holoscript/crdt-spatial, etc. (all `./dist/...` | |
| # exports). The compile step below previously failed with | |
| # `Cannot find module '...core-types/dist/ans.js'` because core-types | |
| # was never built. `pnpm build` builds core first then everything else | |
| # in the correct order (per package.json scripts.build). | |
| - name: Build workspace packages | |
| run: pnpm build | |
| - run: | | |
| pnpm --filter @holoscript/core exec vitest run \ | |
| src/cli/__tests__/holoscript-runner.daemon.test.ts \ | |
| src/cli/__tests__/holoscript-daemon-integration.test.ts | |
| - run: | | |
| mkdir -p .holoscript | |
| pnpm --filter @holoscript/core exec tsx src/cli/holoscript-runner.ts compile \ | |
| ../../compositions/self-improve-daemon.hsplus \ | |
| --target node \ | |
| --output ../../.holoscript/self-improve-daemon.ci-check.js | |
| daemon-pr-branch-run: | |
| name: Daemon PR Branch Run | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| needs: daemon-integration-tests | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - uses: pnpm/action-setup@903f9c1a6ebcba6cf41d87230be49611ac97822e # v6 | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 | |
| with: | |
| node-version: '20' | |
| cache: 'pnpm' | |
| - run: pnpm install --frozen-lockfile | |
| # Build the full workspace — daemon-runner transitively loads from | |
| # @holoscript/core-types, @holoscript/uaal, etc. (all `./dist/...` | |
| # exports). Building only @holoscript/core leaves those un-built. | |
| - name: Build workspace packages | |
| run: pnpm build | |
| - name: Decide whether daemon run is allowed | |
| id: gate | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| run: | | |
| can_run=true | |
| reason="" | |
| if [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ github.event.pull_request.head.repo.fork }}" = "true" ]; then | |
| can_run=false | |
| reason="fork-pr" | |
| fi | |
| if [ -z "$ANTHROPIC_API_KEY" ]; then | |
| can_run=false | |
| reason="missing-anthropic-key" | |
| fi | |
| echo "can_run=$can_run" >> $GITHUB_OUTPUT | |
| echo "reason=$reason" >> $GITHUB_OUTPUT | |
| - name: Snapshot daemon state before run | |
| if: steps.gate.outputs.can_run == 'true' | |
| run: | | |
| mkdir -p .holoscript | |
| if [ -f .holoscript/daemon-state.json ]; then | |
| cp .holoscript/daemon-state.json .holoscript/daemon-state.before.json | |
| else | |
| cat > .holoscript/daemon-state.before.json << 'JSON' | |
| { | |
| "lastQuality": 0, | |
| "totalInputTokens": 0, | |
| "totalOutputTokens": 0 | |
| } | |
| JSON | |
| fi | |
| - name: Run daemon (dry-run, 1 cycle) | |
| if: steps.gate.outputs.can_run == 'true' | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| run: | | |
| start_ts=$(date +%s) | |
| pnpm --filter @holoscript/core exec tsx src/cli/holoscript-runner.ts daemon \ | |
| compositions/self-improve-daemon.hsplus \ | |
| --cycles 1 \ | |
| --model claude-3-5-haiku-20241022 \ | |
| --debug | tee .holoscript/daemon-run.log | |
| end_ts=$(date +%s) | |
| echo "DAEMON_DURATION_SEC=$((end_ts-start_ts))" >> $GITHUB_ENV | |
| - name: Build daemon metrics | |
| if: steps.gate.outputs.can_run == 'true' | |
| run: | | |
| node << 'NODE' | |
| const fs = require('fs'); | |
| const readJson = (f, d) => { try { return JSON.parse(fs.readFileSync(f, 'utf8')); } catch { return d; } }; | |
| const before = readJson('.holoscript/daemon-state.before.json', { lastQuality: 0, totalInputTokens: 0, totalOutputTokens: 0 }); | |
| const after = readJson('.holoscript/daemon-state.json', before); | |
| const metrics = { | |
| qualityDelta: Number((Number(after.lastQuality || 0) - Number(before.lastQuality || 0)).toFixed(6)), | |
| qualityBefore: Number(before.lastQuality || 0), | |
| qualityAfter: Number(after.lastQuality || 0), | |
| cycleDurationSec: Number(process.env.DAEMON_DURATION_SEC || 0), | |
| inputTokensUsed: Math.max(0, Number(after.totalInputTokens || 0) - Number(before.totalInputTokens || 0)), | |
| outputTokensUsed: Math.max(0, Number(after.totalOutputTokens || 0) - Number(before.totalOutputTokens || 0)), | |
| }; | |
| fs.writeFileSync('.holoscript/daemon-metrics.json', JSON.stringify(metrics, null, 2)); | |
| NODE | |
| - name: Daemon metrics summary | |
| if: steps.gate.outputs.can_run == 'true' | |
| run: | | |
| node << 'NODE' | |
| const fs = require('fs'); | |
| let metrics = { | |
| qualityDelta: 0, | |
| qualityBefore: 0, | |
| qualityAfter: 0, | |
| cycleDurationSec: 0, | |
| inputTokensUsed: 0, | |
| outputTokensUsed: 0, | |
| }; | |
| try { | |
| metrics = JSON.parse(fs.readFileSync('.holoscript/daemon-metrics.json', 'utf8')); | |
| } catch { | |
| // best effort summary | |
| } | |
| const lines = [ | |
| '## Daemon Gate Metrics', | |
| '', | |
| `- Quality: ${metrics.qualityBefore} -> ${metrics.qualityAfter} (delta ${metrics.qualityDelta})`, | |
| `- Duration: ${metrics.cycleDurationSec}s`, | |
| `- Tokens: in=${metrics.inputTokensUsed}, out=${metrics.outputTokensUsed}`, | |
| ]; | |
| fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, `${lines.join('\n')}\n`); | |
| NODE | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| if: always() | |
| with: | |
| name: daemon-metrics-${{ github.event.pull_request.number || github.run_id }} | |
| path: | | |
| .holoscript/daemon-state.json | |
| .holoscript/daemon-state.before.json | |
| .holoscript/daemon-metrics.json | |
| .holoscript/daemon-run.log | |
| if-no-files-found: warn | |
| retention-days: 7 | |
| - name: Comment PR with daemon metrics | |
| if: ${{ github.event_name == 'pull_request' && steps.gate.outputs.can_run == 'true' }} | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const marker = '<!-- daemon-pr-metrics -->'; | |
| let metrics = { qualityDelta: 0, qualityBefore: 0, qualityAfter: 0, cycleDurationSec: 0, inputTokensUsed: 0, outputTokensUsed: 0 }; | |
| try { metrics = JSON.parse(fs.readFileSync('.holoscript/daemon-metrics.json', 'utf8')); } catch {} | |
| const body = `${marker} | |
| ## Daemon Metrics (PR Branch Dry-Run) | |
| - Quality delta: ${metrics.qualityDelta} | |
| - Quality: ${metrics.qualityBefore} -> ${metrics.qualityAfter} | |
| - Cycle duration: ${metrics.cycleDurationSec}s | |
| - Token usage: in=${metrics.inputTokensUsed}, out=${metrics.outputTokensUsed} | |
| - Composition: \`compositions/self-improve-daemon.hsplus\` | |
| - Mode: dry-run (no commit)`; | |
| const { owner, repo } = context.repo; | |
| const issue_number = context.payload.pull_request.number; | |
| const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number, per_page: 100 }); | |
| const existing = comments.find(c => c.user?.type === 'Bot' && c.body?.includes(marker)); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }); | |
| } else { | |
| await github.rest.issues.createComment({ owner, repo, issue_number, body }); | |
| } | |
| - name: Write skip summary | |
| if: steps.gate.outputs.can_run != 'true' | |
| run: | | |
| echo "Daemon run skipped: ${{ steps.gate.outputs.reason }}" >> $GITHUB_STEP_SUMMARY | |
| daemon-pr-gate: | |
| name: Daemon PR Gate | |
| runs-on: ubuntu-latest | |
| if: always() | |
| needs: | |
| - daemon-integration-tests | |
| - daemon-pr-branch-run | |
| steps: | |
| - name: Validate daemon checks | |
| run: | | |
| echo "integration=${{ needs.daemon-integration-tests.result }}" | |
| echo "branch_run=${{ needs.daemon-pr-branch-run.result }}" | |
| if [ "${{ needs.daemon-integration-tests.result }}" != "success" ]; then | |
| exit 1 | |
| fi | |
| if [ "${{ needs.daemon-pr-branch-run.result }}" = "success" ] || [ "${{ needs.daemon-pr-branch-run.result }}" = "skipped" ]; then | |
| exit 0 | |
| fi | |
| exit 1 |