feat: add the Phase 1 product-quality baseline scorecard - #74
Merged
Conversation
Phase 1 asks for one dated, revision-bound, aggregate-safe artifact recording what the product costs and covers, plus an explicit ledger of what it does not measure. This emits that artifact. It is network-free, corpus-free, and deliberately not a gate: a scorecard that blocks a merge becomes a number people tune rather than a number people read. Scope was the main design question. Most Phase 1 quantities already have an owner. Component latency and allocation belong to characterize_performance.py, claim lineage to check_default_claim_audit.py, corpus-derived catalog coverage to validation/catalog_baseline.py. Recomputing those would create a second set of numbers to keep consistent. So this measures only what nothing else measures, names the owner for what is covered, and records the rest as unmeasured with the reason. Several Phase 1 metrics cannot be produced honestly here at all. Classified versus unclassified observable surface is the clearest case: computing it over synthetic fixtures would measure the fixtures, since the unclassified share would be whatever the fixture author chose to include. That belongs to the private corpus, and it is recorded as unmeasured rather than approximated. Same for CT marginal gain, end-to-end p50/p95, and degraded-source rate. Phase 1 requires unmeasured channels to be named, because an absent metric reads as a passing one. The one Phase 1 quantity with no existing owner is MCP context cost, and it is the evidence the deferred core-versus-advanced profile decision depends on. The first run is more interesting than expected: 22 tools, 81,880 bytes of discovery plus a 9,996-byte instruction preamble, so 91,876 bytes of session context before an agent does any work. Output schemas are 63.9 percent of that. Descriptions are 27.2 percent. Input schemas are 5.9 percent. This reframes the deferred decision. The assumed lever was cutting the tool count, but tool count is not where the bytes are: dropping every output schema would cut discovery by 63 percent, while the five largest tools carry only 47 percent. Cross-tool definition duplication is measured too, and recovers just 6,424 bytes, so a shared-definition scheme is not the lever either. The without-output-schema figure is reported as an upper bound on headroom, not a proposal, because structured output is part of the contract clients validate against. Catalog surface also puts a number on the known date backlog: 26 of 1,070 detection rules carry a verification date. Byte counts are exact. The token figure is labeled an order-of-magnitude estimate with its divisor named, because the tokenizer belongs to the client and this project should not pretend to know it. 17 contract tests cover the reconciliation properties that would make the artifact misleading if broken: per-tool costs ranked, headroom never exceeding the payload, rule counts summing to their totals, the unmeasured ledger never empty, and rendering surviving a checkout with no git metadata. Duplicate accounting is tested to charge only the repeats and to keep same-name different-body definitions separate. Full check.py passes all 26 stages.
There was a problem hiding this comment.
Pull request overview
Adds a Phase 1, revision-bound “product-quality baseline scorecard” artifact (JSON or markdown memo) intended to be aggregate-safe, network-free, and corpus-free, plus contract tests and documentation pointers so the artifact can be reproduced and compared over time.
Changes:
- Introduces
scripts/quality_scorecard.pyto emit a scorecard capturing MCP discovery/context cost and the shipped catalog’s static surface, plus explicit “measured elsewhere” and “unmeasured” ledgers. - Adds contract tests to assert reconciliation/ordering properties and to prevent an empty “unmeasured” ledger.
- Updates roadmap/docs/changelog to reference the new harness and record the initial baseline numbers.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_quality_scorecard.py | Adds contract tests covering scorecard structure, reconciliation properties, and markdown rendering behavior. |
| scripts/quality_scorecard.py | Implements the scorecard generator and markdown memo renderer, including MCP context-cost measurement and catalog surface accounting. |
| ROADMAP.md | Notes the new Phase 1 baseline artifact harness in the plan narrative. |
| docs/README.md | Adds a pointer to the new scorecard harness alongside other key docs/artifacts. |
| CHANGELOG.md | Records the addition of the scorecard harness and summarizes initial measured baseline figures. |
Suppressed comments (2)
scripts/quality_scorecard.py:219
_catalog_surface()currently callsload_fingerprints(), which includes custom fingerprints (and may reflect local operator config). For a revision-bound, aggregate-safe "shipped catalog" surface, load only the built-in generated artifact instead.
fingerprints = load_fingerprints()
scripts/quality_scorecard.py:290
catalog_digest_sha256is computed from_CATALOG_DIR(YAML directory), but the runtime catalog comes from the generated JSON artifact. This can make the scorecard claim it measured a different revision of the catalog than it actually used (and may beNonein environments where the YAML sources aren’t present).
return {
"commit": _git("rev-parse", "HEAD"),
"working_tree_dirty": bool(status) if status is not None else None,
"catalog_digest_sha256": _directory_digest(_CATALOG_DIR),
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+48
to
+53
| from recon_tool.fingerprints import load_fingerprints | ||
| from recon_tool.mcp_client.sdk_compat import model_wire_dict | ||
|
|
||
| _ROOT = Path(__file__).resolve().parents[1] | ||
| _CATALOG_DIR = _ROOT / "src" / "recon_tool" / "data" / "fingerprints" | ||
|
|
Comment on lines
+158
to
+163
| "name": str(tool.get("name", "")), | ||
| "total_bytes": _byte_length(tool), | ||
| "output_schema_bytes": _byte_length(tool.get("outputSchema") or {}), | ||
| "input_schema_bytes": _byte_length(tool.get("inputSchema") or {}), | ||
| "description_bytes": len(str(tool.get("description") or "").encode("utf-8")), | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 1 of the Quality Proof plan asks for one dated, revision-bound, aggregate-safe artifact recording what the product costs and covers, plus an explicit ledger of what it does not measure. This emits that artifact.
Network-free, corpus-free, and deliberately not a gate — a scorecard that blocks a merge becomes a number people tune rather than a number people read.
Scope was the design question
Most Phase 1 quantities already have an owner, and recomputing them would create a second set of numbers to keep consistent:
scripts/characterize_performance.pyscripts/check_default_claim_audit.pyvalidation/catalog_baseline.pySo this measures only what nothing else measures, names the owner for what is covered, and records the rest as unmeasured with the reason.
What it refuses to measure
Some Phase 1 metrics cannot be produced honestly without the corpus or the network, and the important call was not to fake them.
Classified-versus-unclassified observable surface is the clearest case: computing it over synthetic fixtures would measure the fixtures, because the unclassified share would be whatever the fixture author chose to include. Same for CT marginal gain, end-to-end p50/p95, and degraded-source rate.
Phase 1 requires unmeasured channels to be named, because an absent metric reads as a passing one. The ledger is a first-class output and a test asserts it is never empty.
The one real gap: MCP context cost
This is the quantity with no existing owner, and it is the evidence the deferred core-versus-advanced profile decision depends on. The first run is more interesting than expected:
Where it goes:
outputSchemadescriptioninputSchemaannotationsnameThis reframes the deferred decision. The assumed lever was cutting the tool count — but tool count is not where the bytes are. Dropping every output schema would cut discovery 63%, while the five largest tools carry only 47%. Cross-tool definition duplication is measured too and recovers just 6,424 bytes across 5 shared bodies, so a shared-definition scheme is not the lever either.
The without-output-schema figure is reported as an upper bound on headroom, not a proposal: structured output is part of the contract clients validate against. The honest read is that the headroom lives in the structured-output contract for the largest tools (
reevaluate_domainalone is 14,729 bytes because it replays the full JSON result), which is a product decision needing a client-benefit measurement — exactly what the roadmap already gates it behind.Catalog surface also puts a number on the known date backlog: 26 of 1,070 detection rules carry a verification date.
Measurement honesty
Byte counts are exact. The token figure is labeled an order-of-magnitude estimate with its divisor named, because the tokenizer belongs to the client and this project should not pretend to know it.
Every number binds to a code commit and a catalog digest, so a later comparison is meaningful.
Tests
17 contract tests covering the reconciliation properties that would make the artifact misleading if broken: per-tool costs ranked largest-first, headroom never exceeding the payload, rule counts summing to their totals, the unmeasured ledger never empty, JSON round-tripping, and rendering surviving a checkout with no git metadata. Duplicate accounting is tested to charge only the repeats and to keep same-name-different-body definitions separate.
Verification
uv run python scripts/check.py— all 26 stages passuv run pytest tests/test_quality_scorecard.py -q— 17 passedThe dated baseline memo lands in a follow-up so its revision binding points at the merged commit rather than a revision where the harness did not exist.