Skip to content

chore(deps-dev): bump eslint-plugin-react-hooks from 7.0.1 to 7.1.1 #2311

chore(deps-dev): bump eslint-plugin-react-hooks from 7.0.1 to 7.1.1

chore(deps-dev): bump eslint-plugin-react-hooks from 7.0.1 to 7.1.1 #2311

Workflow file for this run

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