@dogfood-lab/findings
v1.9.0
Published
Finding contract spine for testing-os. Validates, reads, lists, and queries evidence-bound findings — the fourth contract alongside record, scenario, and policy.
Maintainers
Readme
@dogfood-lab/findings
Finding contract spine for testing-os. Validates, reads, lists, and queries evidence-bound findings — the fourth contract alongside record, scenario, and policy.
Part of the testing-os monorepo — the operating system for testing in the AI era.
Findings are the evidence-bound observations produced by dogfood runs. Each finding is anchored to a specific source line, carries severity (CRITICAL / HIGH / MEDIUM / LOW) and status (open / fixed / regressed / etc.) fields, and feeds the four-stage intelligence pipeline: derive → review → synthesize → advise.
Install
npm install @dogfood-lab/findingsCLI
npx @dogfood-lab/findings --help
# Derive findings from records
npx @dogfood-lab/findings derive --records-dir records/ --out findings/
# Review pipeline (apply state transitions, build event log)
npx @dogfood-lab/findings review --findings-dir findings/
# Synthesize patterns + recommendations + doctrine
npx @dogfood-lab/findings synthesize --findings-dir findings/
# Query / advise downstream consumers
npx @dogfood-lab/findings advise --topic <topic>Programmatic surface
import { listFindings, readFinding } from '@dogfood-lab/findings/reader.js';
import { validateFinding } from '@dogfood-lab/findings/validate.js';
const findings = listFindings({ dir: './findings' });
const first = readFinding(findings[0].path);
const result = validateFinding(first);
if (!result.ok) {
console.error(result.errors);
}Pipeline stages
| Stage | Module | Output |
|---|---|---|
| Derive | derive/derive-findings.js | New finding files under findings/; deduplication via derive/dedupe.js |
| Review | review/review-engine.js | Status transitions + event-log.jsonl audit trail |
| Synthesize | synthesis/pattern-derivation.js, synthesis/recommendation-derivation.js, synthesis/doctrine-derivation.js | Pattern, recommendation, doctrine artifacts |
| Review (artifacts) | review/review-artifacts.js | Promotes a synthesis-artifact candidate → accepted so it reaches the advise surface |
| Advise | advise/advice-bundle.js, advise/query.js | Advisory bundles for downstream consumers (e.g., @dogfood-lab/dogfood-swarm) |
Closing the intelligence loop
Synthesis writes patterns, recommendations, and doctrine with status: 'candidate',
but the advise layer (queryPatterns / queryRecommendations / queryDoctrine)
surfaces only accepted artifacts. The artifact review verbs are what promote a
candidate so the intelligence layer actually reaches future projects:
# Promote a derived pattern into the advise surface (accept = the loop closes)
npx @dogfood-lab/findings patterns accept dpat-xxxx --actor mike --reason "sound recurrence"
# Same for recommendations and doctrine
npx @dogfood-lab/findings recommendations accept drec-xxxx --actor mike
npx @dogfood-lab/findings doctrine accept ddoc-xxxx --actor mike
# Retire an accepted pattern when source truth changes (patterns only)
npx @dogfood-lab/findings patterns invalidate dpat-xxxx --actor mike --reason "source changed"
# See what is awaiting review
npx @dogfood-lab/findings patterns queueThe artifact review law reuses the finding status law (review/transitions.js).
Patterns carry a literal invalidated status; recommendations and doctrine do
not, so invalidate is supported for patterns only. review / reopen target
the intermediate reviewed state, which the artifact schemas do not allow — they
are refused honestly rather than writing a schema-invalid artifact.
Re-derivation safety: re-running <type> derive --write will not overwrite an
artifact you have already promoted. A freshly-derived candidate that collides
with an accepted / rejected / invalidated id is preserved, not clobbered
(synthesis/dedupe-artifacts.js, mirroring derive/dedupe.js).
Applying a recommendation back into a policy
An accepted recommendation whose action is a structured add_scenario /
add_check can be applied directly into a named repo policy:
# Preview the change (default — writes nothing)
npx @dogfood-lab/findings recommendations apply drec-xxxx --policy mcp-tool-shop-org/widget
# Apply the structured intent: add the target scenario id to the policy's
# required_scenarios for the recommendation's surface, recording provenance
npx @dogfood-lab/findings recommendations apply drec-xxxx --write --policy mcp-tool-shop-org/widget --actor mikeThis is honest partial automation. Only the structured target id is applied;
the free-text action.details is recorded as provenance and never injected as
policy logic. Free-text-only action types (set_policy, set_evidence, …) and
ambiguous targets (no named policy, multiple surfaces) refuse --write with a
structured { code, message, hint } telling the operator to apply manually.
Finding shape
finding_id: F-XXXXXX-XXX
severity: HIGH
status: open
title: <short imperative-mood phrase>
evidence:
source_pin: { file: <path>, line: <n>, snippet: <verbatim> }
test_pin: { file: <path>, line: <n>, name: <test-name> }
remediation:
approach: <strategy>
rationale: <why>Full shape: @dogfood-lab/schemas/json/dogfood-finding.schema.json.
Atomic I/O (shared discipline)
Internal helpers under lib/:
atomic-write.js— two-phase commit for finding-file writes (writeFileSyncto shadow →renameSyncto canonical)file-lock.js— cross-process advisory lock vialinkSyncCAS, Windows-compatiblerename-with-retry.js— bounded retry on EPERM/EBUSY (Windows AV scanner handle-release window)
These helpers are intentionally shared cross-package discipline. They're exported via the ./lib/* subpath so @dogfood-lab/dogfood-swarm and @dogfood-lab/ingest can reuse the same atomic-write semantics. The CLAUDE.md in the repo root documents the cycle this creates and why it's accepted.
Docs
📖 Full handbook: https://dogfood-lab.github.io/testing-os/handbook/
License
MIT © 2026 mcp-tool-shop
