Changelog
Changelog
Section titled “Changelog”All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Section titled “[Unreleased]”- A/B testing for QualOps config changes: run N config files against the same dataset and compare quality (precision/recall/F1) and cost/latency — for testing per-stage model choices, budgets, prompts, or pipeline modes. New
--config=<repo-relative-path>eval flag runs against an arbitrary config file (validated: inside repo,.json, exists; precedence over--preset), so real ship configs are A/B-tested with no copy to drift.evals/src/run-ab.ts(TypeScript) takes configs via repeatable--config=<path>(same flag name as the qualops CLI), runs the full dataset per arm by default (scope with--dataset/--limit/--repeats, matching a normal eval run), and callsevals/src/compare-experiments.ts, which reads run-logs fromevals/logs/(by path or experiment-label prefix via repeatable--eval-log=<X>) and renders a metric × arm table — one column per arm, N arms supported — with a held-or-up vs. regressed verdict for the 2-arm case. Run-log file/entry types are shared fromrun-log.ts(RunLogFile,ItemCompleteEntry). npm aliases:eval:ab,eval:ab:compare. Documented under “A/B testing configurations” inevals/README.md.
Changed
Section titled “Changed”- Langfuse env vars renamed to
QUALOPS_LANGFUSE_SECRET_KEY/QUALOPS_LANGFUSE_PUBLIC_KEY/QUALOPS_LANGFUSE_BASE_URLso orgs can set them once across repos. BASH_TOOL_DESCRIPTIONconstant removed; callers usebuildBashToolDescription(root)directly so the description always reflects the actual workspace root rather than a hardcoded/workspace/pr.- Updated all dependencies to their latest stable versions (OpenTelemetry,
openai,@openai/agents,@types/node, ESLint +typescript-eslint,jest/ts-jest,prettier,zod; website: Astro 6→7 + Starlight). Dropped the redundant@types/diffstub (diff@9 ships its own types) and pinned transitiveuuidviaoverrides. Build, lint, type-check, unit tests, and evals all pass.
Security
Section titled “Security”- Reduced dependency vulnerabilities: website advisories fully resolved (8→0); remaining root advisories trace to
@eggai/configurable-agent@0.2.1(no upstream fix; slated for retirement). Note: the majority of GitHub’s Dependabot alerts are frozen third-party eval fixtures underevals/datasets/, not shipped dependencies. - Cleared the Semgrep scan: fixed the real findings (shell-injection in the shipped
qualops-npm.ymlexample, unpinned GitHub Actions, missing dependabot cooldown) and suppressed the verified false positives with inlinenosemgrep.
- Hardened the eval A/B tooling’s CLI-path handling: filesystem access is confined to the repo/
evals/logs/(run-log write path,--eval-logreads, and--config/--experimentvalues), and--config/--repeatsare validated up front so a bad arg fails immediately rather than after a costly run. - Bash tool description in
createToolSetnow derives its workspace root fromtoolConfig.bash.workspaceRoot ?? cwd, matching the root the policy enforces. Previously the description always pointed at/workspace/pr, causing every command to be denied withpath-outside-workspacein local environments. git-config-templatesafe.directoryis now derived from the actualworkspaceRootpassed by the session. Previously it was hardcoded to/workspace/prand/workspace/base, causinggitcommands to fail with a dubious ownership error in local environments.GIT_CEILING_DIRECTORIESinenv-scrubis now set to the parent ofworkspaceRootrather than the hardcoded/workspace, so git’s directory-traversal protection is effective in local checkout environments.workspaceRootis now validated against a safe path charset before being interpolated into the git config file. A newline in the value would have allowed config injection (e.g. overridinghooksPath).
Documentation
Section titled “Documentation”- Add TDR 0005 (intent-based agentic review) — reframes false-positive reduction as a review-architecture problem (plan/decompose by intent → execute → aggregate → critique) above the provider-agnostic
AgentAdapter. Status: Rejected (on Opus 4.6). Built and A/B-tested against the flatagenticbaseline on CRB (10 cases):agentic-v2was worse on recall (0.348 vs 0.412) and F1 (0.246 vs 0.299) at ~4× the cost. The result is scoped to Opus 4.6 — smaller models are untested and could differ. TheAgenticExecutorV2code is dropped (not merged; kept only on its implementation branch as a reference). TDR records the evidence, the reject decision, the filtering options to pursue instead, and consequences (cost is a first-class constraint; the config-A/B eval tooling built to test it is the lasting win).
[0.2.7] - 2026-06-18
Section titled “[0.2.7] - 2026-06-18”Changed
Section titled “Changed”- Upgraded eval Langfuse SDK from
langfuse@3.38.20to@langfuse/client@5.4.1. Eval runs are now registered as Langfuse experiments viadatasetRunItems.create()with a consistentrunName, grouping all items from a single eval run under one experiment entry in the Langfuse UI.
- Agentic adapters now use SDK-native structured output (same
detectCapabilities()path as the file-by-file pipeline) instead of heuristic JSON text parsing.AnthropicAdapterpassesoutputFormat: {type: "json_schema"},OpenAIAdapterusesoutputType, andConfigurableAgentAdapterusesoutput: {structured: true}— all gated onisUnstructured(). The text fallback path is kept for unstructured models and now restores the pre-QUALOPS-18 trailing-comma fix. Runs that return non-empty non-JSON output now throw instead of silently producing zero findings. --diff-filterparameter inlistChangedFilesagentic tool now validated against the allowed git diff-filter character set ([ACDMRTUXB*]), consistent with howbase/headrefs are validated viaisSafeGitRef.AIProviderTypeenum infactory.tswas missingOPENAI_COMPATIBLE, causing theopenai-compatibleswitch case to use a raw string literal and bypass the exhaustivenevercheck in thedefaultbranch.
[0.2.6] - 2026-06-17
Section titled “[0.2.6] - 2026-06-17”baseUrlfield added toaiStageConfigschema foropenai-compatibleproviders, withOPENAI_BASE_URL/OPENAI_API_KEYenv-var fallbacks resolved ingetResolvedStageConfig.openai-compatibleprovider support for agentic review mode via@eggai/configurable-agent(Vercel AI SDK v5 agent loop). Any provider with a custombaseUrlcan now run the full agentic security audit without SDK-specific adapters.- Provider-dialect smoke spec:
npm run test:smokeruns the 4 AI caller stages migrated in PR #145 (file-reviewer,validation-resolver,dedup-resolver,root-cause-extract) against each real provider (anthropic,openai,bedrock,github) using a slice fixture as input. Validates that the structured-output dialect path returns a zod-validated response without throwing. Implemented as a Jest spec undertests/smoke/with its ownjest.smoke.config.ts— not picked up by defaultnpm test(whoserootsare limited totests/unit/). Provider config comes fromConfigService+ the existingPROVIDER_DEFAULTStable, not a duplicated table. Providers with missing credentials aredescribe.skip()-ed; providers with malformed credentials fail loudly via the provider class’s ownvalidateApiKey(). Input is a slice fixture underevals/datasets/inbox/smoke-sql-injection/, loosely following TDR 0002. Nightly + manual CI workflow at.github/workflows/provider-dialect-smoke.yml. Automates the unchecked manual smoke item from PR #145’s test plan; distinct from the deferred per-stage golden-evals item which validates output quality. unstructureddialect for LLM models withoutresponse_format: {type: "json_schema"}support (e.g. Llama, Qwen2.5, DeepSeek-V3, Phi, older o1-series). WhenisUnstructured()is true, the pipeline runs a full prose path:ProseFileReviewer→ProseValidationResolver→ProseDeduplicationResolver→session/prose-report.md. No JSON parsing, no structured schemas — the model writes free-form prose and subsequent stages refine it in-kind.- Bundled litellm model capability snapshot (
src/ai/providers/model-capabilities.json, 2101 chat models) for automaticsupportsResponseSchemalookup. Unknown models default tounstructured(safe). scripts/update-model-capabilities.tsmaintenance script to sync the snapshot from upstream litellm (npm run maintenance:update-model-capabilities [--write]).
[0.2.5] - 2026-06-08
Section titled “[0.2.5] - 2026-06-08”- Azure OpenAI keys (non-
sk-format) no longer fail validation whenOPENAI_BASE_URLis set. Thesk-prefix check is now correctly skipped for custom endpoints.
[0.2.4] - 2026-06-04
Section titled “[0.2.4] - 2026-06-04”skipPatternsconfig field is now fully functional as a pre-filter: excluded files never reach the review pipeline in file-by-file mode, and agentic tool calls (read_file,grep_files,glob_files) enforce patterns at the handler layer for both OpenAI and Anthropic providers.- Anthropic agentic mode now uses MCP tools for file access instead of SDK built-ins, ensuring
skipPatternsenforcement is consistent across providers. globFilestool upgraded fromfind-based toglobnpm package for proper**glob support.- Default
skipPatternsinConfigServicechanged from infrastructure dirs to empty ([]) — patterns are project-specific and should be set per project. qualops’s own.qualopsrc.jsonnow lists its TS-specific patterns. - Removed
file-exclusions.ts(dead code —applyPenalty()was never called).
[0.2.3] - 2026-05-28
Section titled “[0.2.3] - 2026-05-28”Changed
Section titled “Changed”- CRB evals are now self-contained slice directories (
evals/datasets/crb/<id>/slice.json+repo/) — no external repo cloning required. Replacedfetch-crb-dataset.tswithcheck-crb-staleness.tswhich validates local slices against upstream CRB PR URLs. - Neutralize language-specific wording in built-in prompts where the underlying tooling is genuinely language-agnostic, so review output is no longer TypeScript-flavored when qualops is pointed at a non-TS repo.
- Bump
@anthropic-ai/claude-agent-sdkfrom 0.2.139 to 0.3.144. - Bump
@anthropic-ai/claude-agent-sdk-linux-x64from 0.2.139 to 0.3.144. - Bump
@opentelemetry/sdk-nodefrom 0.217.0 to 0.218.0. - Migrated all
evals/JavaScript files to TypeScript: scorers, schemas, llm-client, config, reviewer, run-log, upload-datasets, recall-report, fetch-crb-dataset, jest config, and all spec files.
npm-publishworkflow: removenpm install -g npm@lateststep.
[0.2.2] - 2026-05-19
Section titled “[0.2.2] - 2026-05-19”- Zero-config mode: run
qualopswith justANTHROPIC_API_KEYorOPENAI_API_KEYand no.qualopsrc.json. Provider is auto-detected (Anthropic takes priority), defaulting to an agentic review with all built-in subagents. - Bundled default prompt (
src/config/prompts/review/quality.md) and agent placeholder (src/config/agents/) shipped with the package and used as fallbacks via layered search paths. - Native LLM structured-response support across all stages (QUALOPS-18). Replaces fragile fenced-JSON parsing with provider-native structured output: OpenAI
response_format: json_schema(strict mode where supported) +json_objectfallback; Anthropicoutput_config(Claude 4.5+) + forcedtool_usefallback; Bedrock forcedtool_usewithinput_schema. Schema is the single source of truth — zod definitions emit JSON Schema with field descriptions transmitted to the model via the structured channel; responses are parsed and validated by zod automatically. - New
BaseAIProviderconsolidating shared token accounting + cost computation while preserving exact per-provider semantics (OpenAIprompt_tokensincl. cached, Anthropic/Bedrockinput_tokensexcl. cached; Bedrock log policy unchanged). - New
ProviderCapabilitiesdescriptor that routes(provider, model)to the right structured-output dialect, replacing model-name string sniffing. - Reusable zod schemas in
src/ai/shared/schemas/for review issues, validation results, dedup indices, search/replace fixes, and root-cause classifications. - Agentic mode now supports OpenAI and Azure OpenAI providers via
@openai/agents. Setprovider: "openai"in your stage config to use the OpenAI adapter; setOPENAI_BASE_URLto an Azure endpoint and the correct Azure client is used automatically. - You can now specify a model and provider together in stage config using
model: { provider: "openai", name: "gpt-4o" }instead of relying on a separate top-levelproviderfield. - OpenTelemetry observability instrumentation across the full review pipeline (file-by-file, agentic, and eval runs), with auto-detection for Langfuse and generic OTLP backends. All span attributes are sanitized to prevent credential leakage.
- Agentic jobs now support a
promptfield for file-based prompt instructions, combined with the existing inlinesystemPrompt - GitHub Models AI provider (
provider: "github") viahttps://models.github.ai/inference - Zod-based runtime validation for
.qualopsrc.jsonwith deprecation warnings for legacy fields - JSON Schema generated from Zod schemas (
npm run generate:schema) replacing hand-maintained schema - Eval
--severityfilter to run only CRB cases with matching golden comment severity - Report on eval flakiness for Code Review Benchmark
npm run eval:recall-reportwith filtering options-- --severity=critical init-claudenow scaffolds a validated default config, quality prompt, and supports--providerflag- New
Promote to Stableworkflow (workflow_dispatch) for promoting a beta release to a clean stable version - New
update-beta-refandupdate-stable-refjobs in the npm publish workflow that force-move thebeta/stablelightweight git tags after each release docs/tdr/folder for Technical Design Records, with TDR 0001 documenting the release process- New
Releasespage on the docs site explaining the two-tier model to consumers
Changed
Section titled “Changed”AIProvider.completeis now overloaded:complete<S extends z.ZodType>(opts & { schema: S })returnsAIResponse<z.infer<S>>(schema-typed); plaincomplete(opts)still returnsAIResponse<string>.AIMessage.cacheControlis now a typed first-class field (replaces runtime'cache_control' in msniff in the Anthropic provider).- All migrated callers (
file-reviewer,validation-resolver,dedup-resolver,fix-generator,root-cause-extract) now use schema-drivencomplete. Hand-written<response_format>prompt blocks removed; semantic rules moved into zod.describe()annotations. - Upgrade TypeScript from 5.9 to 6.0 with tsconfig migration (
moduleResolution: bundler,baseUrlremoval) - Upgrade eslint from 9.x to 10.x, migrate
eslint-plugin-importtoeslint-plugin-import-x - Release process: introduce two-tier
@beta/@stablemodel.betaandstableare movable lightweight git tags, force-moved by CI on each publish or promotion. Seedocs/tdr/0001-release-process.mdand the rewritten Release Process section ofCONTRIBUTING.md Create Release PRworkflow now deletes its half-createdrelease/v*branch on failure- Release failure issues now include the failing stages and release kind (beta vs stable)
- Normalize
uses: eggai-tech/qualops@v1examples across the README, docs, and example workflows to@stable - Refactor agentic tools:
tools/index.tsis now a provider-agnostic registry (createToolSet); Anthropic and OpenAI SDK wiring stays inside their respective adapters - AI provider types/factory now include
githuband use stricter provider typing - Environment config and test setup now include
GITHUB_API_KEY - Update documentation to reference the new JSON Schema and provide configuration examples
- Added eval suite
Removed
Section titled “Removed”- Deleted
JsonParserclass and the duplicated privatefixMalformedJson(last production callers migrated). - Deleted misnamed
src/ai/shared/structured-ai.ts(relocateddetectFrameworkContexttosrc/shared/utils/framework-detector.ts). - Removed dead
completeWithStructureinterface method (never used in production).
- Fix GitHub Action post-integration step
- Update the logger config loading to read from
${cwd}/.qualops/.qualopsrc.jsoninstead of CWD - Remove unused
promptfoodevDependency - Fix lint failure with
typescript-eslint8.58+ due to unused type predicate parameter - npm publish workflow now passes
--tag betaon pre-release versions so that thelatestdist-tag is not clobbered by betas - CI changelog gate now treats
release/v*-beta.NPRs like ordinary PRs (requires entries under[Unreleased]instead of a versioned heading), so beta release PRs pass CI - Movable
beta/stabletag pushes now use an explicit-SHA--force-with-leaseso the push succeeds on every release after the first (lightweight tags have no remote-tracking ref for the implicit lease to use) - Release-branch cleanup-on-failure now only runs when this workflow run actually pushed the branch (sentinel via
$GITHUB_ENV), so a pre-existingrelease/v*branch is never deleted by a failed run - Release version validation now allows only the prerelease labels the publish workflow recognises (
rc,alpha,beta); unrecognised labels like0.3.0-preview.1are rejected up-front instead of silently publishing tolatest Promote to Stableworkflow now asserts thatstable_versionequalsbeta_version’s base (e.g.,0.4.0-beta.1can only promote to0.4.0)
[0.2.1] - 2026-03-14
Section titled “[0.2.1] - 2026-03-14”Changed
Section titled “Changed”- Release workflows: migrate from PAT to GitHub App token with auto-publish on merge
- Pin all GitHub Actions to SHA digests for supply chain security
- Enable npm trusted publishing with OIDC provenance (repo now public)
- Replace softprops/action-gh-release with native gh CLI
- Script injection vulnerabilities in CI and release workflow inputs
- Remove unnecessary contents:write permission from dependabot auto-merge
- EOF heredoc injection in changelog extraction (random delimiter)
- Add npm pre-flight check for idempotent publish retries
- Add failure notification job (auto-creates GitHub issue on release failure)
[0.2.0] - 2026-03-14
Section titled “[0.2.0] - 2026-03-14”- Resolve all npm audit vulnerabilities (diff, @aws-sdk/client-bedrock-runtime, transitive deps)
- Release PR workflow: add Node.js setup, sync package-lock.json after version bump
- Fix script injection vulnerability in release workflow version inputs
- Fix
@aggai/qualopspackage name typo in qualops-llm.txt
- Dependabot integration with grouped updates and auto-merge for patch/minor
- Agentic reviewer mode using Claude Agent SDK for PR-level analysis
- Context preloading: inject diffs/content directly into agent prompt (70% fewer tool calls)
- Cross-file dependency tracing with
find_usagestool - Security analysis subagent for vulnerability detection
- Custom agent support via configuration or markdown files in
.qualops/agents/
Changed
Section titled “Changed”- Release workflows: migrate from PAT to GitHub App token, auto-publish on release PR merge
- Refactor qualops-llm.txt: add multi-provider support, updated models/pricing, 47% size reduction
- Upgrade all AI SDKs: @anthropic-ai/sdk 0.78, openai 6, claude-agent-sdk 0.2, zod 4
- Upgrade GitHub Actions: checkout v6, setup-node v6, upload-artifact v7, download-artifact v8
- Pipeline jobs now support
mode: 'file-by-file' | 'agentic'configuration - Extended
AgenticConfigwithcontextMode,maxTokensPerFile,maxTotalTokensoptions init-claudecommand now bundles LLM context locally (works with private repos)- Updated documentation with agentic mode examples and configuration
[0.1.1] - 2025-01-06
Section titled “[0.1.1] - 2025-01-06”.qualops/folder structure for configuration and examples- LLM context distribution with
init-claudecommand - GitHub integration with PR comments and checks API
Changed
Section titled “Changed”- Moved configuration from root
.qualopsrc.jsonto.qualops/.qualopsrc.json - Moved examples to
.qualops/examples/
- ESLint and Prettier configuration alignment
[0.1.0] - 2025-01-05
Section titled “[0.1.0] - 2025-01-05”- Initial release of QualOps
- Multi-stage pipeline: analyze, review, fix, report, judge
- AI-powered code review with Claude, GPT-4, and AWS Bedrock support
- Framework-specific documentation loading (Angular, NgRx, RxJS, OWASP)
- Session-based review with context management
- Fix suggestion generation with automatic application
- HTML and Markdown report generation
- GitLab CI integration with MR comments
- GitHub Actions integration
- Extract log caching for incremental analysis
- Configurable thresholds and quality gates