Skip to content

Attest upstream facts in the gate and re-check them before merging (design v8 8.2) - #232

Merged
tzhouam merged 1 commit into
mainfrom
kb/local-upstream-facts
Sep 29, 2026
Merged

tzhouam merged 1 commit into
mainfrom
kb/local-upstream-facts

Conversation

@tzhouam

@tzhouam tzhouam commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

Design v8 §8.2: upstream fact attestations. The service signs them and the publisher re-checks them independently before merging. Until now nothing checked what rules claim about their upstream. The service signed facts=[], and the verifier refused any verdict carrying facts.

What counts as a claim (knowledge_service/facts.py)

  • ^[PR #N] citations: the PR must be merged.
  • Backticked paths whose first segment is a top-level entry of the upstream tree: the file or directory must exist.
  • path::Symbol (including root files such as setup.py::main): the symbol must be defined.
    • Python files are parsed with ast. Only real def/class, bound assignment targets and imports count; text in strings or comments, d[k] = v and obj.attr = v do not.
    • Other files fall back to line patterns.
  • Not claims: fragments like config/x.py, bare words, and ../... segments.
  • Which claims must hold:
    • Active rules: every claim.
    • Retired rules: claims are only recorded, but the PR named as retirement evidence must be merged.

Service

  • run_gate(facts=...) attests right after L1:
    • a false claim fails before any paid L2 call, and goes to refine;
    • an unreadable upstream sends the change to people.
  • The pinned {repository, sha} and the facts are signed into the verdict.
  • The observer is the service's own bare mirror, shared with the release sweep, plus the read-only GitHub reader.

Publisher

  • After the local gate passes, it re-observes every fact from its own mirror and gh api, using upstreams from its own adapter config.
  • It does not merge this round on any of: a difference, a missing public upstream, or an unreadable upstream.
  • The service re-signs next round. After 24 consecutive hours it marks the change gate_failed and tells people.

Measured

On the live vllm-omni knowledge against upstream main:

  • 710 active rules; 68 path/symbol claims.
  • 7 rules (1%) cite paths that no longer exist, for example benchmarks/serve.py and vllm_omni/core/prefix_cache.py. Those rules fail the gate when next edited.

Tests

  • test_kb_upstream_facts.py:
    • extraction, including root files and dot segments;
    • AST definitions, with strings, subscripts, attributes and imports;
    • attest/recheck when the upstream agrees, disagrees, is unreachable, has an unknown SHA or is another repository, and when facts are forged;
    • a real git mirror;
    • the gate failing before L2;
    • the publisher: merges when the upstream agrees; refuses and escalates after 24 hours; refuses without a configured public upstream.
  • Full suite green locally, except the 3 known test_mcp.py environment failures, which fail identically on origin/main.
  • Commit hook (gpt-6-astra-high): APPROVE at iteration 3. Iterations 1–2 found root-file symbols, docstring definitions and subscript targets; all are fixed and tested.

🤖 Generated with Claude Code

…esign v8 8.2)

Rules state facts about their upstream: the PRs they cite were merged, and
the files and symbols they name exist. Until now nothing checked them. The
service signed facts=[], and the verifier refused any verdict that carried
facts.

- New knowledge_service/facts.py, shared by both sides. It extracts the
  claims that can be resolved from a rule:
  - ^[PR #N] citations;
  - backticked paths whose first segment is a top-level entry of the
    upstream tree;
  - path::Symbol, where every dotted part must be defined by a def, class
    or assignment.
  Fragments such as config/x.py, and paths with .. or ... segments, are not
  claims. An active rule's claims must hold. A retired rule's claims are
  only recorded, but the PR named as retirement evidence must be merged.
  attest() observes the claims on the upstream head and returns the pinned
  {repository, sha}, the facts and the problems. recheck() observes every
  signed fact again and compares it field by field. An unreadable upstream
  raises FactsError.
- Service: run_gate(facts=observer) attests right after L1, so a false
  claim fails before any paid L2 call and goes to refine. An unreadable
  upstream sends the change to people. The facts and the pinned upstream
  go into the decision and are signed into the verdict. The observer
  (kb_service/upstream_facts.MirrorObserver) uses the service's own bare
  mirror, shared with the release sweep, and the read-only GitHub reader.
  KbRuntime.upstream_facts supplies it. A repository without an upstream,
  or one that publishes nothing, attests nothing.
- Publisher: after the local gate passes and before the final control
  re-read, it re-checks every fact from independent sources: its own
  mirror and `gh api`, using upstreams from its own adapter config. Any
  difference, a missing public upstream, or an unreadable upstream means
  no merge this round.
- The service treats such a refusal as transient and signs again next
  round, recording facts_failing_since. After 24 consecutive hours the
  change goes to gate_failed and people are told. Any other answer ends
  the run of failures.
- gate_verifier no longer refuses verdicts that carry facts: the
  publisher re-checks them.

Measured on the live vllm-omni knowledge against upstream main: 710 active
rules, 68 path/symbol claims, 7 rules (1%) cite a path that no longer
exists. Those rules fail the gate when they are next edited, which is the
intent.

Tests: test_kb_upstream_facts.py covers claim extraction, symbol
definitions, and attest/recheck when the upstream agrees, disagrees, is
unreachable, has an unknown SHA, is another repository, or the facts are
forged. It also covers a real git mirror and a gate that fails before L2.
For the publisher: it merges when the upstream agrees; it does not merge
when the upstream disagrees or is unreachable, and escalates after 24
hours; it does not merge without a configured public upstream.

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

chatgpt-codex-connector Bot commented Sep 29, 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-29T07:40:53.619601Z c6bf5a9 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 b98ffbb into main Sep 29, 2026
2 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: c6bf5a9566

ℹ️ 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".

Comment on lines +85 to +88
if symbol:
if SYMBOL.fullmatch(symbol) and not path.endswith("/"):
out.append(Claim("symbol", active, path=path, symbol=symbol))
continue

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 Normalize call notation before filtering symbol claims

When a changed rule uses common call notation such as path.py::func(), SYMBOL.fullmatch fails and the unconditional continue drops the token entirely, so neither the path nor the symbol is attested and an invalid upstream assertion can pass the gate. This is already used by the active EXEC-10c rule in knowledge/repos/vllm-omni/components/model-executor/rules-platform-backends.md:32; editing that rule would therefore bypass the new check. Normalize supported call notation (and similar grouped notation), reject it explicitly, or at minimum preserve the path claim.

Useful? React with 👍 / 👎.

Comment on lines +29 to +33
try:
proc = subprocess.run(["git", "--git-dir", str(self.git_dir), *args], capture_output=True,
text=True, check=False, timeout=600)
except (OSError, subprocess.TimeoutExpired) as exc:
raise FactsError(f"git {args[0]}: {exc}") from exc

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 Convert blob decoding failures into FactsError

When a referenced upstream file contains non-UTF-8 bytes—including valid Python source with a non-UTF-8 encoding declaration—subprocess.run(..., text=True) raises UnicodeDecodeError while decoding stdout. _git catches only OSError and TimeoutExpired, whereas run_gate handles only FactsError, so the exception escapes and aborts the gate instead of producing the intended human result for an unreadable upstream. Decode explicitly with a defined policy or translate decoding failures to FactsError.

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