feat: add contribution-aware evaluation scope and file metadata - #361
Merged
ArthurCRodrigues merged 12 commits intoAug 21, 2026
Conversation
Add iter_test_results() and to_score_vector() to ResultTree to project nested result nodes into a flat map keyed by stable path strings (category/subject/.../test_name -> raw score float). Store score_vector as a JSON column in SubmissionResult and expose it across API schemas and endpoints (SubmissionResponse, SubmissionDetailResponse, ExternalResultCreate). Populate score_vector in grading_service.py during _persist_success for successful pipeline runs, while remaining null for failed or interrupted runs. Add Alembic migration 003_add_score_vector_column.py, 16 unit tests in test_score_vector.py, and comprehensive documentation in docs/features/score_vector.md and docs/API.md. Design decision: FocusService tree traversal was deliberately kept separate because Focus relies on cumulative weight multiplier propagation for impact sorting, whereas score_vector is a raw score projection across all nodes.
…-network/autograder into 357-feature-score-vector
Add ResultComparator and ComparisonResult to compare a head grading execution against a baseline result_tree from a previous submission without secondary pipeline runs or code execution. Add ResultTree.from_dict() classmethods across all result tree node models to reconstruct a ResultTree from a serialized dictionary. Compute score_delta and test_deltas with status classifications (improved, regressed, unchanged, introduced, removed) for all test paths. Persist comparison JSON column in SubmissionResult table via Alembic migration 004_add_comparison_column_to_submission_results.py. Expose baseline_result_tree in SubmissionCreate input schema and surface comparison in SubmissionResponse, SubmissionDetailResponse, and ExternalResultCreate schemas and endpoints. Add 9 unit tests in test_result_comparator.py and documentation in docs/features/baseline_comparison.md, docs/API.md, and mkdocs.yml.
matheusmra
marked this pull request as ready for review
July 27, 2026 22:01
Member
|
@matheusmra let's add a contribution related test to static analysis template in order to better visualize if these changes are working fine. I'll create an issue soon.
|
11 tasks
Member
Author
Alright 👍 |
ArthurCRodrigues
approved these changes
Aug 21, 2026
ArthurCRodrigues
enabled auto-merge
August 21, 2026 15:15
ArthurCRodrigues
deleted the
355-refactor-contribution-aware-evaluation
branch
August 21, 2026 15:22
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.
Summary
This pull request introduces contribution-aware evaluation context without coupling the core grading engine to Git, GitHub, repositories, commits, pull requests, or any other platform-specific concept.
The implementation adds two typed inputs that the pipeline is allowed to understand:
EvaluationScope, which identifies the submitted files that are the primary subject of the current evaluation.SubmissionFile.changed_lines, which identifies the one-indexed lines that were added or modified inside a file.It also adds
SubmissionFile.metadataas an opaque per-file passthrough channel. The pipeline transports this metadata to test functions but never interprets it. This allows callers to attach information such as a raw patch, a change status, provider data, review annotations, or other repository-derived context without expanding the core model every time a new integration needs additional data.All new fields are optional and default to
None. Existing callers that submit ordinary context-free snapshots continue to use the same behavior.Stack and base branch
This is intentionally a stacked pull request.
357-feature-score-vector355-refactor-contribution-aware-evaluationThe local branch was created from the head of #360. Targeting
357-feature-score-vectorkeeps this review limited to the contribution-aware evaluation work instead of including the six commits and approximately 1,400 lines already under review in the parent pull request.After #360 is merged, this pull request can be retargeted or rebased onto
mainwithout changing the intended feature diff.Problem
Before this change, a submission was only a filename-to-file mapping. Every file was treated as an equally relevant snapshot, and the pipeline had no typed way to answer either of the following questions:
This limitation prevented callers from efficiently evaluating a delta while still sending complete file contents. It also encouraged platform integrations to consider adding repository-specific concepts directly to the core engine, which would violate the architectural boundary between the general-purpose autograder and its adapters.
The implementation resolves that tension by separating typed pipeline inputs from opaque integration context.
Architectural principles
The core understands evaluation scope, not source-control systems
EvaluationScopecontains only a list of filenames. It does not contain repository names, commit identifiers, branch names, pull request numbers, commit messages, compare references, or provider-specific data.This keeps the
autograder/package reusable for any caller that needs delta-aware evaluation, including callers that derive scope from an uploaded archive, an editor session, a content management system, a code review platform, or another source.Typed fields are reserved for data that pipeline steps actively read
The pipeline actively reads:
EvaluationScope.scoped_filesSubmissionFile.changed_linesThese values therefore have explicit types and documented behavior.
Domain context remains opaque
SubmissionFile.metadatais typed only asOptional[Dict[str, Any]]. The pipeline does not inspect its keys or values.Test functions can access the same metadata through:
SubmissionFile.metadataon each object in thefilesargument.file_metadatamapping passed throughexecute(**kwargs).Submission-level API
metadataremains a passive audit and correlation record stored assubmission_metadata. It is not converted into a typed core pipeline field and is not read during grading.Detailed implementation
Core submission models
SubmissionFilenow supports:changed_lines: Optional[Set[int]]metadata: Optional[Dict[str, Any]]is_contribution_aware, which reports whether changed-line information was explicitly supplied.An explicitly empty changed-line set is different from missing information. The convenience property therefore checks
changed_lines is not Noneinstead of relying on truthiness.The new
EvaluationScopedataclass contains:Submissionaccepts an optionalevaluation_scopewith aNonedefault.Pipeline execution
PipelineExecution.evaluation_scopeexposes the scope from the active submission. This keeps access consistent for pipeline steps while preserving a single source of truth onSubmission.Structural analysis
StructuralAnalysisStepnow:scoped_filesto a set for efficient membership checks.An explicit empty scope results in no files being parsed. An absent scope retains the previous all-files behavior.
StructuralAnalysisResultnow includes:The field uses an empty-dictionary factory and was added after the existing defaulted fields to preserve compatibility for callers that may construct the dataclass positionally.
Grading and test-function context
GradeStepforwardsPipelineExecution.evaluation_scopetoGraderService.GraderServiceforwards it intoSubmissionGrader.For every test node,
SubmissionGrader:TestFunction.execute()with bothevaluation_scopeandfile_metadata.The runtime pipeline values take precedence over same-named criteria parameters, consistent with the existing handling of injected runtime context.
Filtering
file_metadatabyfile_targetprevents unrelated file context from leaking into tests that are intentionally scoped to a subset of the submission.Web API schema
Each submitted file can now include:
{ "filename": "service/payment.py", "content": "full file content", "changed_lines": [88, 89, 90], "file_metadata": { "change_status": "modified", "patch": "@@ -88,6 +88,12 @@" } }Submission requests can also include:
{ "evaluation_scope": { "scoped_files": [ "service/payment.py", "tests/test_payment.py" ] } }file_metadatais deliberately named differently from top-levelmetadataat the API boundary:file_metadatabecomesSubmissionFile.metadataand is available to tests.metadatabecomes databasesubmission_metadataand remains passive provenance.Web persistence and hydration
The submission endpoint stores
changed_linesandfile_metadatawith each file record and serializes the Pydantic evaluation scope to a plain dictionary for the background grading request.The grading service reconstructs:
changed_lineslists asSet[int]file_metadataasSubmissionFile.metadataEvaluationScopedataclassMissing fields remain
None, preserving compatibility with submissions created before this feature.End-to-end data flow
The complete flow is:
PipelineExecutionexposes the evaluation scope.StructuralAnalysisStepparses only scoped, eligible source files.StructuralAnalysisResultcarries parsed roots and changed-line sets.GradeStepforwards scope and structural context.SubmissionGraderresolves each test's file target.At no point does the core engine need to know how the caller calculated the scope or metadata.
Backward compatibility
This change is additive.
SubmissionFile.changed_linesdefaults toNone.SubmissionFile.metadatadefaults toNone.Submission.evaluation_scopedefaults toNone.GraderService.grade_from_tree()acceptsevaluation_scope=None.SubmissionGraderacceptsevaluation_scope=None.StructuralAnalysisResult.changed_linesdefaults to an empty dictionary.None.**kwargs.No scoring rules, result-tree behavior, sandbox behavior, template registration, or test-function contracts were removed or changed incompatibly.
Test coverage
The test suite covers the feature at each architectural boundary.
Model coverage
is_contribution_awarebehavior for missing and explicitly empty changed-line sets.Pipeline coverage
PipelineExecution.evaluation_scope.GradeStepforwarding the same scope instance into the grader service.StructuralAnalysisResult.Grader coverage
TestFunction.execute().file_target.SubmissionFileobjects.API and service coverage
GradingRequest.EvaluationScope.Rich repository metadata regression coverage
tests/unit/test_file_metadata_passthrough.pyexercises a realistic, deeply nested repository metadata payload containing:The test verifies that:
file_metadataDocumentation
The documentation now explains:
Validation
The final committed state was validated with:
Pylint completed successfully for all changed Python files. The dedicated rich metadata regression test received a 10.00/10 pylint score. The broader changed-file lint run retained only the repository's existing advisory complexity and test-docstring warnings and exited successfully.
git diff --checkalso completed without whitespace errors.Reviewer guidance
A useful review order is:
autograder/models/dataclass/submission.pyfor the public core model.autograder/steps/structural_analysis_step.pyfor scope behavior.autograder/services/grader/criteria_grader.pyfor test-function context.web/schemas/submission.pyandweb/service/grading_service.pyfor the API round trip.tests/unit/test_file_metadata_passthrough.pyfor the strongest end-to-end metadata invariant.Out of scope
This pull request does not:
SubmissionFile.metadataThose behaviors can be implemented by adapters or individual test functions using the general context introduced here.
Closes #355