Skip to content

Company eval packs and meta-harness scoring contract

This is the product-surface contract for cross-company evaluation. A company supplies an eval pack that says what good looks like; HSM-II supplies the shared runner, trace capture, KPI verifiers, scoring, frontier ranking, refiner artifacts, and promotion gates.

For the plain-language product layer on top of this contract, see Agent quality cockpit. The short version: enterprise-grade evaluation underneath, simple quality packs, agent health, run stories, and promotion gates on top.

The current implementation lives in:

  • scripts/meta-harness/pack_loader.py — loads pack YAML and normalizes tasks.
  • scripts/meta-harness/meta_harness.py — runs Company OS agent-chat candidates and writes results.json, summary.json, NDJSON traces, frontier state, and refiner artifacts.
  • scripts/meta-harness/evaluate_turn.py — executes one task and combines process score with KPI score.
  • scripts/meta-harness/kpi_verifier.py — deterministic Company OS state and trace verifiers.
  • schemas/company_eval_pack.v1.json — versioned schema for the YAML contract.
  • docs/company-os/examples/company-eval-pack.example.yaml — runnable example accepted by the current loader.

Eval pack YAML

Pack files are YAML documents that validate against schemas/company_eval_pack.v1.json. The loader currently requires company, personas, tasks, and scoring; newer optional fields are safe documentation for humans and future verifier plugins.

yaml
schema_version: hsm.company_eval_pack.v1

company:
  id: 0b1aeb33-4f4e-4c70-8d83-a66d087e24c5
  name: Agentsys Engineering
  api_base: http://127.0.0.1:3847

personas:
  - cto
  - staff-engineer
  - qa-release-lead

tasks:
  - label: task_create_release_blocker
    persona: cto
    prompt: >
      Create a high-priority task for the release lead to investigate the latest
      production build blocker, include acceptance criteria, and set a due date.
    scorer: task_state
    weight: 1.25
    slice_tags: [task-write, release, governance]
    kpis:
      task_created: true
      assigned_to: qa-release-lead
      has_due_at: true
      priority_min: 7
      spec_keywords: [production, build, blocker, acceptance]
    safety:
      requires_human: false
      max_tool_calls: 8

  - label: memory_grounded_recent_work
    persona: staff-engineer
    prompt: >
      Summarize what we have been working on recently using Company OS memory.
    scorer: memory_grounding
    weight: 1.0
    slice_tags: [memory, grounding]
    kpis:
      memory_tool_called: true
      grounded_in_memory: true
      answer_keywords: [Company OS, harness]

  - label: validate_delivery_tools
    persona: qa-release-lead
    prompt: Run validate-delivery and give a pass/fail verdict with the blocking reason.
    scorer: composite
    weight: 1.0
    slice_tags: [tool-use, qa]
    kpis:
      tool_sequence_contains: [validate-delivery]
      answer_keywords: [pass, fail, blocker]
      min_answer_length: 80

scoring:
  process_weight: 0.35
  kpi_weight: 0.65
  min_mean_score: 0.75
  min_finalize_rate: 1.0
  max_error_rate: 0.0
  max_mean_latency_s: 90
  pareto_filter: true
  promotion:
    require_no_regression: true
    shadow_runs: 3
    rollback_on: [quality_drop, finalize_drop, safety_failure]

Run it with:

bash
python3 scripts/meta-harness/pack_loader.py docs/company-os/examples/company-eval-pack.example.yaml
python3 scripts/meta-harness/meta_harness.py --pack docs/company-os/examples/company-eval-pack.example.yaml --iterations 1

Field semantics

FieldMeaningCurrent implementation
schema_versionVersioned contract identifier.Documented by schema; current loader tolerates packs without it for backward compatibility.
company.idCompany OS company ID used for API calls and task context.Overrides the default --company-id unless CLI explicitly sets a different company.
company.api_baseCompany OS API base for state verifiers.Passed to evaluate_turn.py --api-base.
personasAllowed persona slugs for tasks.pack_loader.py rejects tasks whose persona is absent.
tasks[].labelStable task ID used in trace filenames and reports.Must be unique in the pack.
tasks[].promptUser-facing task prompt sent to agent-chat.Passed directly to the Company OS chat route.
tasks[].scorerVerifier binding.Must be one of task_state, memory_grounding, tool_sequence, stigmergic, answer_keywords, or composite.
tasks[].kpisScorer-specific assertions.Passed to kpi_verifier.py.
tasks[].weightRelative importance.Loaded today; weighted aggregation is a next implementation slice.
tasks[].slice_tagsAnalysis slices such as memory, tool-use, finance, or handoff.Schema-defined; slice aggregation is a next implementation slice.
scoring.process_weightShare of final turn score from process/trace behavior.Passed to evaluate_turn.py.
scoring.kpi_weightShare of final turn score from post-run KPI verification.Passed to evaluate_turn.py; weights must sum to about 1.0.
scoring.min_mean_scoreOffline quality gate.Applied by meta_harness.py through --min-mean-score.
scoring.min_finalize_rate / max_error_rate / latency and cost capsPromotion gates for reliability and efficiency.Partly implemented (min_finalize_rate CLI exists); remaining gates belong in the promotion command.

Scorer contract

Each scorer returns a float in [0.0, 1.0]. Deterministic checks are preferred over LLM judges when the Company OS state or NDJSON trace can answer the question.

ScorerVerifiesExample KPI keys
task_stateA task was created or changed with the expected assignment, priority, state, due date, and specification content.task_created, assigned_to, has_due_at, priority_min, spec_keywords, task_state_after
memory_groundingThe answer used Company OS memory tools and overlaps with real shared memory.memory_tool_called, grounded_in_memory, answer_keywords
tool_sequenceThe trace used required tools, exact tool order when needed, and non-hallucinated tool keys.tool_sequence_contains, tool_sequence_exact, no_hallucinated_tool_key
stigmergicThe run left useful notes or coordination residue in the Company OS graph.note_written, note_keywords
answer_keywordsThe final answer contains required terms and enough substance.answer_keywords, min_answer_length
compositeRuns all applicable deterministic scorers and averages them.Any combination of the keys above

Turn score

For a task from a pack:

text
process_score = evaluate_turn trace score
kpi_score     = deterministic verifier score
turn_score    = process_weight * process_score + kpi_weight * kpi_score

The process score covers agent-chat behavior such as finalization, error state, tool evidence, and basic trace quality. The KPI score checks business outcomes after the turn by querying Company OS APIs and inspecting the NDJSON trace.

Aggregation contract:

text
candidate.mean_score       = weighted mean(turn_score by task.weight)
candidate.finalize_rate    = finalized turns / total turns
candidate.error_rate       = error turns / total turns
candidate.mean_latency_s   = mean(latency_s)
candidate.mean_tool_calls  = mean(tool_calls)
slice[tag].mean_score      = weighted mean over tasks containing tag

The in-tree aggregate() function currently computes unweighted means over result rows. Weighted means and slice aggregates are the intended next patch now that pack fields are explicit.

Frontier and promotion

The meta-harness should rank candidates on a Pareto frontier, not just a single average. A candidate is promotion-eligible only if it satisfies all hard gates:

  • mean_score >= scoring.min_mean_score
  • finalize_rate >= scoring.min_finalize_rate
  • error_rate <= scoring.max_error_rate
  • mean_latency_s <= scoring.max_mean_latency_s when set
  • mean_cost_usd <= scoring.max_mean_cost_usd when cost telemetry exists
  • no required safety assertion failed

Among eligible candidates, Pareto dimensions are:

text
maximize: mean_score, finalize_rate
minimize: error_rate, mean_latency_s, mean_cost_usd, mean_tool_calls when tool count is a cost proxy

Promotion flow:

  1. Run the pack offline against a candidate.
  2. Write results.json, summary.json, per-task NDJSON, and refiner artifacts.
  3. Score deterministic KPIs and record failure tags.
  4. Compare against baseline and current frontier.
  5. If hard gates pass, run shadow or limited rollout according to scoring.promotion.shadow_runs.
  6. Promote only through company_os_promote_delta.py or a successor service that writes a promotion record and rollback condition.

The safety invariant from docs/EVAL_AND_META_HARNESS.md still applies: eval-side winners do not change live runtime behavior until an explicit integration maps them into runtime prompt policy, skill dispatch, memory policy, or guarded Company OS configuration.

Failure taxonomy

Every failed or weak turn should be tagged so the result is actionable:

TagTrigger
tool_missingRequired tool absent from trace.
tool_wrong_argsTool was called with missing or invalid key arguments.
state_not_changedExpected Company OS object was not created, updated, routed, or resolved.
ungrounded_answerAnswer lacks required memory/data grounding.
handoff_pendingRun delegated work but counted pending handoff as completion.
finalize_missingAgent did not emit the expected final response.
latency_over_budgetRuntime exceeded pack latency cap.
cost_over_budgetCost telemetry exceeded pack cost cap.
safety_gate_failedSafety assertion failed or required human gate was bypassed.

Failure tags should flow into summary.json, proposer context, refiner deltas, and promotion records so the loop can improve specific behavior instead of chasing opaque scores.

Governance & instrument integrity (the layer external eval frameworks omit)

Every published agent-eval framing (offline/online, Pareto, failure taxonomy, promotion service) assumes the verifier is ground truth. This program proved on real data that it is not: task_state read 0.25 confidently for an extended period and was 100% scaffolding artifact. The platform's distinguishing contract is therefore not "we score agents" — it is "the score is attributable and you can tell when it is lying before you act on it."

The three-place scorer invariant

scorer is one contract enforced in three places; drift between them is a governance defect, not a doc nit:

  1. schemas/company_eval_pack.v1.json — the documented enum.
  2. scripts/meta-harness/pack_loader.py:VALID_SCORERS — the loader gate.
  3. Postgres CHECK ck_company_eval_packs_scorer — the persistence gate.

A scorer added to one must be added to all three in the same change. (This section exists because the schema had drifted out of sync with the live code and CHECK — found and reconciled rather than papered over.)

Provenance / drift units

A score is only governable if attributable to the revision that produced it. Three parallel drift units, same discipline:

  • company_eval_runs.scorer_version — git sha of the harness that scored.
  • company_memory_entries.policy_version — memory-policy sha (Phase M0).
  • company_eval_runs.skill_version — content hash of the skill body/path (Phase S0). NOTE: company_skills.body is empty in some companies (content lives on disk at skill_path); S1 must hash the real on-disk content, not a naive sha(body) which collapses to one constant.

Runs across different versions of any unit are not directly comparable; the dashboard surfaces a drift banner instead of trending across them.

Falsifiable governance, human-checked

  • company_eval_kill_criteria — pre-registered falsifiable conditions evaluated against run history; fire → dashboard flag, never auto-revert.
  • company_eval_reviews — sampled human review; the override rate is the measured convincing-vs-correct signal (≥20% → scorer suspect). An override cannot be recorded without stating the correct verdict.

The online / shadow / auto-promotion wire is deliberately GATED

External guidance lists "promotion service → shadow → online evals → auto rollback" as a primitive to build next. This program's evidence says the opposite for any high-stakes domain: an automated promote/rollback loop on a verifier that can be confidently wrong compounds bad learning at machine speed (the Continual-Harness "monotonic accumulation" property, double-edged).

The substrate exists and its interlocks have fired on real data. It is Panel-1 (human-in-the-loop) by design: the refiner proposes (apply_policy: review_only), kill-criteria flag, humans promote. Enabling autonomous promotion is a customer risk decision taken only with override-rate low and kill-criteria/drift green — not a default and not a "next primitive." Selling or shipping it as automatic would be the exact failure this contract exists to refuse.

HSM-II documentation built with VitePress