Skip to content

Add knowledge intake, the quality gate and judge calibration - #207

Merged
tzhouam merged 1 commit into
mainfrom
kb/intake-and-gate
Sep 28, 2026
Merged

tzhouam merged 1 commit into
mainfrom
kb/intake-and-gate

Conversation

@tzhouam

@tzhouam tzhouam commented Sep 28, 2026

Copy link
Copy Markdown
Collaborator

Fifth PR (5a) of the knowledge-base redesign: intake, the quality gate and judge calibration. The merge flow, snapshot activation, release sweep and scheduler come next (5b).

What changes

  • Pinned models, no fallback:
    • Defaults are generator claude-code:claude-opus-5-5 and judge codex:gpt-6-sol:medium (configurable via KB_GENERATOR / KB_JUDGE).
    • The judge must come from a different model family.
    • An unavailable, timed-out or malformed reply leaves the work queued, or uses one of the bounded repair rounds; it never switches to a weaker model.
    • Every model call is recorded in $KB_STATE_DIR/traces/model_calls.jsonl.
  • Providers: complete() takes an effort pin. Codex passes it as model_reasoning_effort and validates it.
  • Sources: read-only reads of the knowledge repository clone, GitHub (merged PRs paginated with a (merge time, PR number) cursor; bounded PR evidence; releases; tags) and the host's Copilot run-lesson inbox.
  • Intake:
    • The generator drafts typed operations with the owner pages and existing rules in context, and the evidence fenced as untrusted data.
    • Every destination must be inside the repository, and apply_operations must accept the result, with up to two repair rounds fed the exact error.
    • Conflicting drafts stay pending; exhausted repairs are marked rejected, not "no rules".
  • Gate:
    • L1, then a per-block L2 judge asking only the dimensions each block kind needs, then a consistency check per owner directory.
    • Any "no" or conflict fails. Any "unsure", unavailable model, protected rule, external reference or circuit breaker (per repository) goes to people.
    • If L1 fails, no model is called.
  • Signed verdict schema (kb-gate-verdict/1), vendorable.
  • Concurrency: intake runs under the single-writer ledger lease with a keeper thread renewing it. Every write is fenced on lease ownership, so a worker that lost its lease writes nothing.
  • Workflow: the kb-intake playbook (candidate, not visible to the planner) runs collect → intake → publish.
    • In shadow mode it records what it would publish.
    • In auto_merge mode it writes a signed open_pr outbox item, but only with a stored, passing calibration for the exact judge and case set.
  • Calibration: kb calibrate requires every bad case to be caught and at most 20% of good ones rejected. The vllm-omni set ships in its adapter:

Test Plan

  • test/test_kb_intake_gate.py (35 tests):
    • pinned roles and cross-family enforcement;
    • codex effort flag; gateway fail-closed and call recording;
    • repair loop, cross-repo destinations refused, malformed generator/judge replies;
    • gate pass/fail/human/breaker/L1 short-circuit;
    • end-to-end with a real temporary git repo and fake GitHub, in shadow and in auto_merge with and without calibration;
    • the generator unavailable (work waits); the human path;
    • conflicting drafts and rejections; pagination and a tied-timestamp cursor;
    • the lease: contention, renewal, takeover during judging, a late generator failure;
    • kb run --state-dir without mutating the environment;
    • calibration scoring and the playbook's registration.
  • Full suite, check_spec_freshness --strict, check_doc_links.

Test Result

  • Full suite passed. SPEC 89/89 strict. Doc links OK.
  • Local review gate (gpt-6-astra-high): approved after 9 rounds of fixes, each covered by a regression test.
  • Not yet run: kb calibrate against the real judge. That needs the bot host's Codex login and is an owner step before any repository leaves shadow mode.

🤖 Generated with Claude Code

The knowledge service can now turn upstream changes into gated knowledge
change sets. The merge flow, activation, sweep and scheduler build on this.

- models: generator and judge pinned as provider:model[:effort], by default
  claude-code:claude-opus-5-5 and codex:gpt-6-sol:medium. The judge must come
  from a different model family. An unavailable, timed-out or unparseable
  model raises ModelUnavailable and the work stays queued; there is no weaker
  fallback. Every call is recorded with inputs, outputs and usage.
- providers: complete() takes an `effort` pin. codex passes it as
  model_reasoning_effort and validates it; the others accept and ignore it.
- sources: read-only reads of the knowledge repository clone (knowledge and
  external texts via git cat-file), GitHub (merged PRs, bounded PR evidence,
  releases, tags) and the host's Copilot run-lesson inbox.
- intake: the generator drafts typed operations (add, edit_same_meaning,
  replace, retire). It sees the owner pages and existing rules and gets the
  evidence fenced as untrusted data. apply_operations must accept the
  result, with up to two repair rounds fed the exact error.
- gate: L1, then a per-block L2 judge answering only the dimensions each
  block kind needs, then a consistency check per owner directory. Any "no" or
  conflict fails; any "unsure", unavailable model, protected rule, external
  reference or circuit breaker (counted per repository) goes to people. L1
  failure makes no model call.
- verdict: the signed kb-gate verdict schema (identity binding, a patch
  manifest compared by pre/post blob IDs, block and consistency judgements,
  upstream facts, auto vs human-approved), vendorable.
- intake runs under the single-writer ledger lease. A keeper thread renews it
  during slow model calls, and every ledger write goes through one
  transaction fenced on lease ownership, so a worker that lost its lease
  stages nothing. Discovery uses a (merge time, PR number) cursor over all
  result pages. Conflicting drafts stay pending; exhausted repairs are
  `rejected`, not "no rules".
- runtime and the kb-intake playbook (candidate, planner-invisible):
  collect, intake, publish. Shadow records what it would open; auto_merge
  writes a signed open_pr outbox item. `kb run` runs it through the standard
  executor.
- calibration: `kb calibrate` scores the judge on the repository's set; every
  bad case must be caught and at most 20% of good ones rejected. vllm-omni's
  set ships in its adapter: the 8 human-approved rules of #202 as known-good,
  plus 10 known-bad (mutations and the historical MMH3-4e contradiction), each
  with real upstream evidence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-28T11:55:30.425565Z 763c8da PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@tzhouam
tzhouam merged commit c6f5a95 into main Sep 28, 2026
3 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 763c8da890

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

an edit that changes meaning, an invented API) and the historical bot-written
MMH3-4e that contradicted MMH3-4a (folded by a human in 03b0b62a2).

Evidence is the real upstream PR (title, body, bounded diff excerpt).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove persisted raw PR evidence from calibration fixtures

The calibration cases check full upstream PR bodies and diff excerpts into the adapter tree, even though the repository contract requires raw PR material to remain temporary and outside the repository. Replace these replay fixtures with compliant evaluation artifacts or externally supplied evidence rather than shipping the raw PR payloads in every installation.

AGENTS.md reference: AGENTS.md:L12-L17

Useful? React with 👍 / 👎.

validate(data)
except Exception as exc: # any malformed shape is a controlled refusal
raise ModelUnavailable(f"{role.label()} reply failed its schema: {exc!r}") from exc
return ModelReply(role, data, text, record["served_model"], record["usage"], seconds)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject replies that do not match the pinned model

When a transport returns a successful reply whose model differs from role.model—for example because the backend silently substituted a fallback—the gateway records the mismatch but still accepts the response. This breaks the no-fallback pin and is especially unsafe for the judge because calibration is subsequently stored under the requested label, allowing the substituted model to authorize auto-merge; fail closed on a nonempty incompatible served-model value.

Useful? React with 👍 / 👎.

return "calibration_required"
data = rt.load_changeset_files(changeset_id)
detail = changeset["detail"]
rt.outbox.issue(lifecycle.repo, "open_pr", {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Make open-PR publication idempotent

If the process exits after issue() writes the uniquely identified signed open_pr item but before the later changeset status update, the changeset remains gated; retrying or resuming publication therefore emits another independently executable item for the same changeset. Concurrent calls have the same race, so the publisher can receive duplicate GitHub actions; persist a deterministic issuance marker/status atomically with the outbox operation or otherwise deduplicate by changeset.

Useful? React with 👍 / 👎.

from .runtime import record_calibration

case_dir = lifecycle.adapter_dir / lifecycle.calibration_set
report = run_calibration(case_dir, gateway=ModelGateway(Settings()), judge=judge)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Record calibration model calls in the service trace

When operators run kb calibrate, this gateway is constructed without the recorder used by KbRuntime, so none of the calibration prompts, replies, served models, or usage are written to $KB_STATE_DIR/traces/model_calls.jsonl. This leaves the model calls that authorize auto-merge unaudited despite the documented every-call trace contract; construct the gateway with the same state-directory recorder.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant