Skip to content

fix(issues): allow local issues create/add_message without a Bear… #1402

fix(issues): allow local issues create/add_message without a Bear…

fix(issues): allow local issues create/add_message without a Bear… #1402

Workflow file for this run

name: CI test lanes
on:
pull_request:
push:
branches:
- main
- dev
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-test-lanes-${{ github.ref }}
cancel-in-progress: true
jobs:
baseline:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Init inspector submodule (Vitest imports inspector sources)
run: git submodule update --init --depth 1 inspector
- name: Build @neotoma/client, @neotoma/agent, and cursor-hooks (Vitest hook tests)
run: |
npm ci --prefix packages/client
npm run build --prefix packages/client
npm ci --prefix packages/agent
npm run build --prefix packages/agent
npm ci --prefix packages/cursor-hooks
npm run build --prefix packages/cursor-hooks
# opencode-plugin imports @neotoma/client via "file:../client" — its
# tests import src/ directly so no build step is needed, but the
# node_modules symlink must exist for ESM resolution.
npm ci --prefix packages/opencode-plugin
- name: Type check
run: npm run type-check
- name: Lint
run: npm run lint
- name: Format check
run: npm run format:check
# Must run AFTER format:check so a stale committed file cannot be silently
# re-formatted by `openapi:generate` (which runs prettier --write) before
# the format gate sees it. Do not move this step above format:check.
- name: OpenAPI types are in sync with openapi.yaml
run: |
npm run openapi:generate
if ! git diff --quiet -- src/shared/openapi_types.ts; then
echo "::error::src/shared/openapi_types.ts is out of sync with openapi.yaml. If you edited openapi.yaml, run 'npm run openapi:generate' and commit the regenerated src/shared/openapi_types.ts."
git --no-pager diff -- src/shared/openapi_types.ts | head -100
exit 1
fi
- name: Lint site copy
run: npm run lint:site-copy
- name: Build server (required by CLI tests)
run: npm run build:server
- name: Run unit Vitest suite (no DB)
run: npm run test:unit
- name: Validate Playwright coverage map
run: npm run validate:coverage
- name: Validate automated test catalog
run: npm run validate:test-catalog
- name: Fetch release tags (capability manifest is derived from vX.Y.Z tags)
run: git fetch --tags --force
- name: Validate capability manifest
run: npm run validate:capability-manifest
- name: Validate documentation dependencies
run: npm run validate:doc-deps
contract_parity:
# Focused PR-time cross-surface contract-parity lane.
#
# Retrospective ent_68a9270e2e656da847c10ced: the `source_storage:'reference'`
# feature shipped incomplete to an evaluator across three releases because the
# covering integration tests ran only in the nightly remote_integration
# workflow — never on the PR baseline lane, which runs `test:unit` (no DB).
#
# This lane runs ONLY the parity-critical store/reference integration tests
# (MCP + REST surfaces driven through one shared scenario matrix) on every PR.
# It deliberately does NOT pull the whole nightly integration suite onto each
# PR — just the cross-surface parity gate — so it stays fast (~a few seconds
# of tests). The tests run against the same local SQLite backend the nightly
# job uses (default mode: RUN_REMOTE_TESTS unset), with file-parallelism
# disabled to avoid SQLite lock contention between the two HTTP-server files.
#
# Implements task_policy cross_surface_contract_parity_tested_all_surfaces
# (ent_2ad0677fe23c0c1878ae43e8) and
# fixed_means_behavior_verified_not_contract_accepted (ent_db0b7855d47012084477fb00).
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build server (integration tests boot the in-process Actions server)
run: npm run build:server
- name: Run cross-surface contract-parity integration tests
run: npm run test:contract-parity
frontend:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run frontend Vitest suite
run: npm run test:frontend
site_export:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Validate route parity
run: npm run validate:routes
- name: Build UI
run: npm run build:ui
- name: Install Playwright browser (Chromium)
run: npx playwright install --with-deps chromium
- name: Validate locale parity
run: npm run validate:locales
- name: Build site export
run: npm run build:site:pages
- name: Validate site export
run: npm run validate:site-export
agentic_evals:
# Tier 1 agentic-eval matrix. Runs all fixtures against the cursor-hooks
# adapter using an in-process mock Neotoma server — no API key required.
# Live-model cells (claude-code-plugin, codex-hooks, etc.) are Phase 1
# stubs and skip automatically. Add ANTHROPIC_API_KEY to repo secrets and
# remove this comment when live harnesses graduate from stub status.
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build @neotoma/client and cursor-hooks
run: |
npm ci --prefix packages/client
npm run build --prefix packages/client
npm ci --prefix packages/cursor-hooks
npm run build --prefix packages/cursor-hooks
- name: Run Tier 1 agentic evals
run: npm run eval:tier1
eval_scenarios:
# eval-harness SCENARIO lane (Tier 2). Runs the agent-driving scenarios in
# packages/eval-harness/scenarios/ against a real in-process isolated
# Neotoma server in REPLAY mode (committed cassettes — no API key required).
# This is the CI gate for tool-behavior evals (agent calls correct / merge /
# subscribe / …), which the hook-lifecycle agentic_evals lane cannot model.
# Quarantined scenarios (meta.quarantine, tracked by neotoma#1726) are
# skipped, not failed; cassette-less scenarios skip until recorded.
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run eval-harness scenarios (replay)
run: npm run eval:scenarios
eval_combined:
# COMBINED layered-matrix lane (WRIT state layer + Tier-2 agent layer).
# Runs packages/eval-combined against a real isolated Neotoma server and
# emits the per-category WRIT×Tier-2 coverage matrix — the qa-gate surface
# for the QA-evals reframe (QE2): a regression in EITHER layer fails the
# lane. Needs the writ submodule checked out + built; runs without an API
# key (WRIT degrades to a substring judge; OPENAI_API_KEY/WRIT_JUDGE_API_KEY
# gives higher-fidelity llm_judge scoring). The runner fails closed if a
# requested WRIT run errors (neotoma#1738/#1739).
runs-on: ubuntu-latest
steps:
- name: Checkout (with writ submodule)
uses: actions/checkout@v4
# NOT submodules:recursive — other submodules (e.g. foundation) use SSH
# URLs that fail in CI. writ is public HTTPS, so init ONLY it explicitly.
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Init writ submodule (public HTTPS)
run: git submodule update --init writ
- name: Install dependencies
run: npm ci
- name: Build writ (eval-combined imports writ/dist)
run: |
npm ci --prefix writ
npm run build --prefix writ
- name: Run combined WRIT + Tier-2 matrix
run: npm run eval:combined
python_sdk:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install neotoma-client with dev dependencies
run: pip install -e "packages/client-python[dev]"
- name: Run Python unit tests
run: pytest packages/client-python/tests/ -v
security_gates:
# Gate G1–G3 + manifest sync (Track 1 of the pre-release security gates).
# Always runs the manifest sync check + auth matrix; runs the static
# rules + classifier output on every PR so reviewers see the
# security-sensitive concerns before merge.
# Source plan: .cursor/plans/pre-release_security_gates_44e01d74.plan.md
# Threat model: docs/security/threat_model.md
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Classify diff (G1)
run: |
BASE_REF=""
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE_REF="origin/${{ github.event.pull_request.base.ref }}"
else
BASE_REF="origin/main"
fi
git fetch origin "${BASE_REF#origin/}" --depth=200 || true
npm run security:classify-diff -- --base "$BASE_REF" --head HEAD --json > /tmp/security_classify_raw.txt 2>&1
# Strip npm script header lines before parsing JSON
grep -v "^>" /tmp/security_classify_raw.txt | grep -v "^npm" > /tmp/security_classify.json || true
cat /tmp/security_classify.json
SENSITIVE=$(node -e "console.log(require('/tmp/security_classify.json').sensitive)")
echo "SECURITY_SENSITIVE=$SENSITIVE" >> "$GITHUB_ENV"
- name: Static rules (G2 — security:lint)
run: npm run security:lint
- name: Protected routes manifest sync (G3 + manifest-sync)
run: npm run security:manifest:check
- name: Topology auth matrix (G3)
run: npm run test:security:auth-matrix
- name: Surface sensitivity flag
if: always()
run: |
echo "Security-sensitive diff: ${SECURITY_SENSITIVE:-unknown}"
echo "Per docs/security/threat_model.md, sensitive PRs MUST land with a Security hardening supplement section and a same-release security_review.md."