GCMD_Keyword_Classifier_MVP / IMPLEMENTATION_PLAN.md
igerasimov's picture
MVP Milestone 3
413d5a1
|
Raw
History Blame Contribute Delete
55.8 kB

A newer version of the Gradio SDK is available: 6.20.0

Upgrade

GCMD Science Keyword Classifier MVP Implementation Plan

1. MVP Objective

Build the smallest tested MVP that demonstrates constrained, progressive GCMD Science Keyword classification from article title and abstract:

Topic -> Term -> Variable_Level_1 -> Variable_Level_2 -> Variable_Level_3

The MVP must prove that selecting only from application-supplied direct-child candidates produces structurally valid, scientifically defensible recommendations without forcing unsupported specificity. The fixed deterministic target is 100% vocabulary validity for accepted classifications.

2. Scope

The MVP includes:

  • Recursive loading of data/gcmd_hierarchy.json.
  • Canonical vocabulary records and hierarchy lookup structures.
  • Article loading and validation from data/articles.json.
  • DOI as the unique article identifier.
  • Exact preservation of source article fields: DOI, Title, Year, and Abstract.
  • Multi-label Topic routing.
  • Dynamic Term routing within each selected Topic.
  • Variable-level controlled descent using direct children only.
  • Stopping at any supported UUID-bearing level.
  • Multiple independent classifications per article.
  • Structured model responses using application-supplied candidate IDs.
  • Application-side construction of UUIDs, labels, hierarchy levels, and paths.
  • Deterministic validation for every final classification.
  • Redundancy removal.
  • Valid no-classification results.
  • Batch processing, incremental JSON persistence, basic caching, retries, and logging.
  • Lightweight Gradio demonstration separated from core classification logic.
  • Hugging Face compatibility through a future thin root-level app.py.

3. Non-Goals and Deferred Features

Deferred unless explicitly approved:

  • Independent semantic validation as a separate model task.
  • Production-grade human-review UI.
  • Vector databases and embedding-based retrieval.
  • External bibliographic lookup, full-text retrieval, CMR integration, and automated vocabulary synchronization.
  • Advanced model-comparison infrastructure, confidence calibration, risk-based policies, distributed processing, and production authentication.
  • Evaluation dashboards and production deployment infrastructure.

The MVP should include extension points for these features but should not implement them prematurely.

4. Current Repository Assessment

Current structure:

.
├── AGENTS.md
├── IMPLEMENTATION_PLAN.md
├── PROJECT_SPEC.md
├── README.md
├── data/
│   ├── articles.json
│   └── gcmd_hierarchy.json
├── prototype/
│   └── app_hf_poc.py
├── schemas/
├── src/
└── tests/

Observations:

  • IMPLEMENTATION_PLAN.md was empty before this plan.
  • pyproject.toml is not present.
  • src/, schemas/, and tests/ are empty skeleton directories.
  • README.md is brief and describes the broad project goal, not implementation details.
  • prototype/app_hf_poc.py is a frozen proof-of-concept baseline and must not be modified.
  • data/gcmd_hierarchy.json root is a non-UUID Category named EARTH SCIENCE.
  • Current full-data smoke inspection observed 14 UUID-bearing Topics, 144 Terms, 1,368 Variable_Level_1, 1,456 Variable_Level_2, and 553 Variable_Level_3 records, with no duplicate UUIDs observed. These are assertions about the current source file, not generic application invariants.
  • Topic direct-child counts range from 4 to 25 Terms; Term direct-child counts range from 0 to 52; Variable child counts range up to 28 and 16. This supports direct-child prompting without retrieval for the initial MVP.
  • Current full-data smoke inspection observed 468 articles in data/articles.json, with required keys present in sampled records and no duplicate DOIs observed. This count is a current-data assertion, not a generic application invariant.

GCMD Vocabulary Scope and Expected Growth

This project classifies concepts only within the EARTH SCIENCE Category represented by data/gcmd_hierarchy.json.

All current Topic and Term nodes are UUID-bearing and assignable.

The vocabulary file is updated periodically as new concepts are added, typically by only a small number of terms per week. The loader and index must therefore discover all concepts dynamically at runtime and must not hard-code counts, names, UUIDs, or branches.

The current vocabulary size is representative of the expected operational scale. The MVP does not need to design for an order-of-magnitude increase in vocabulary size. Current record counts remain useful full-data smoke-test assertions, while the application logic must continue to work when ordinary incremental vocabulary updates change those counts.

5. Proof-of-Concept Reuse and Replacement Analysis

Reusable ideas from prototype/app_hf_poc.py:

  • Hugging Face-compatible Gradio launch pattern.
  • Simple text-entry interface for title and abstract.
  • Multi-label Topic routing.
  • Structured Pydantic model responses.
  • Model-backed classification using an OpenAI-compatible chat model.
  • Basic validation of model selections against application-provided options.
  • Branch fan-out concept for multiple selected Topics.

Conflicts with the MVP:

  • The prototype loads gcmd_hierarchy.json from the repository root, while the MVP should use data/gcmd_hierarchy.json.
  • It builds Topic-wide complete-path lists and asks the model to extract complete paths. The MVP must use progressive direct-child classification.
  • It treats paths as strings selected by the model, while the MVP must construct authoritative records from a vocabulary index.
  • It builds a static LangGraph node for every Topic, while the MVP should use reusable services and dynamic branches.
  • It hard-codes gpt-4o in classification functions.
  • It combines data loading, index creation, model interaction, graph orchestration, validation, and UI in one file.
  • It uses a fallback pseudo-topic instead of a formal no-classification result.
  • It does not preserve article records, batch process articles.json, cache results, save incrementally, or emit the formal structured JSON result.

Components to replace:

  • Replace Topic-wide path extraction with direct-child routers.
  • Replace string-path validation with UUID/candidate-ID validation against a canonical vocabulary index.
  • Replace global mutable prototype state with injectable services.
  • Replace in-file Gradio classification logic with gcmd_classifier.ui calling application services.
  • Replace hard-coded model construction with a provider-neutral model interface and settings.

6. Key Architectural Decisions

  • Use simple Python orchestration for MVP rather than LangGraph. The workflow is recursive and branch-based, but the required branching can be represented clearly with service calls, queues, and model fakes. LangGraph may be reconsidered after the MVP if observability or complex branch recovery needs justify it.
  • Use candidate IDs generated by the application for prompts. Candidate IDs can be stable within a prompt, such as c1, c2, while authoritative UUIDs remain application-owned.
  • The model must never generate final UUIDs, canonical paths, hierarchy levels, or labels.
  • Include all direct children in prompts by default. Candidate limits should be configurable, but retrieval/pre-ranking is deferred unless a measured prompt limit is exceeded.
  • Treat every UUID-bearing node as assignable, including internal nodes with children.
  • Treat the known non-UUID root Category as routing/context only. Topic routing selects UUID-bearing Topic records supplied by the vocabulary index; the root Category is never selectable. Any other UUID-less hierarchy node is an error unless an approved vocabulary rule explicitly permits it.
  • Use Pydantic for internal schemas and structured model responses.
  • Keep independent semantic validation out of the MVP implementation unless separately approved; retain schema fields or metadata hooks where needed for later compatibility.
  • Introduce root-level app.py only when the Gradio interface exists and is tested. It should import from src/gcmd_classifier/ui/ and launch the app.
  • Keep source JSON files read-only. Generated outputs, caches, schemas, and logs must be stored separately.

7. Proposed Repository and Package Structure

.
├── .env.example
├── AGENTS.md
├── IMPLEMENTATION_PLAN.md
├── MVP_PROGRESS.md
├── PROJECT_SPEC.md
├── README.md
├── app.py
├── data/
│   ├── articles.json
│   └── gcmd_hierarchy.json
├── prototype/
│   └── app_hf_poc.py
├── pyproject.toml
├── schemas/
│   ├── classification_result.schema.json
│   └── run_summary.schema.json
├── src/
│   └── gcmd_classifier/
│       ├── __init__.py
│       ├── config.py
│       ├── errors.py
│       ├── logging_config.py
│       ├── models.py
│       ├── articles/
│       │   ├── __init__.py
│       │   └── loader.py
│       ├── classification/
│       │   ├── __init__.py
│       │   ├── candidates.py
│       │   ├── redundancy.py
│       │   ├── traversal.py
│       │   ├── routers.py
│       │   └── validator.py
│       ├── llm/
│       │   ├── __init__.py
│       │   ├── base.py
│       │   ├── fake.py
│       │   ├── openai_provider.py
│       │   ├── prompts.py
│       │   └── schemas.py
│       ├── persistence/
│       │   ├── __init__.py
│       │   ├── cache.py
│       │   └── json_store.py
│       ├── pipeline/
│       │   ├── __init__.py
│       │   ├── batch.py
│       │   └── service.py
│       ├── ui/
│       │   ├── __init__.py
│       │   └── gradio_app.py
│       └── vocabulary/
│           ├── __init__.py
│           ├── index.py
│           └── loader.py
└── tests/
    ├── fixtures/
    │   ├── articles_valid.json
    │   ├── articles_invalid.json
    │   └── gcmd_hierarchy_small.json
    ├── test_articles.py
    ├── test_cache.py
    ├── test_output_schema.py
    ├── test_pipeline.py
    ├── test_redundancy.py
    ├── test_structured_responses.py
    ├── test_validation.py
    └── test_vocabulary.py

Justification:

  • vocabulary/ owns deterministic GCMD loading, canonical records, and lookup structures.
  • articles/ owns read-only article parsing and validation.
  • llm/ isolates providers, prompts, structured schemas, retries, and test fakes.
  • classification/ owns candidate construction, routing logic, traversal, redundancy removal, and deterministic validation.
  • pipeline/ coordinates article-level and batch workflows without UI concerns.
  • persistence/ owns incremental result storage and cache identity.
  • ui/ owns Gradio only and calls pipeline services.
  • schemas/ stores formal output schemas, separate from runtime Python models.

8. Component Responsibilities

  • config.py: typed settings for paths, model provider/name, parameters, retries, timeouts, prompt versions, output paths, cache options, and candidate limits.
  • errors.py: explicit exception classes for malformed vocabulary, malformed articles, duplicate UUIDs, duplicate DOIs, invalid candidate IDs, model failures, output validation, and persistence errors.
  • models.py: shared Pydantic models for canonical records, articles, classification records, results, metadata, warnings, and errors.
  • vocabulary.loader: read JSON, recursively traverse variable-depth hierarchy, calculate file hash, detect malformed nodes and duplicate UUIDs.
  • vocabulary.index: UUID lookup, path lookup, parent lookup, direct-child lookup, Topic-to-Term lookup, Term-to-Variable lookup, descendant/ancestor checks.
  • articles.loader: load articles, validate required fields, preserve source values exactly, detect duplicate DOIs.
  • llm.base: provider-neutral interface returning typed responses plus usage/cost metadata.
  • llm.openai_provider: optional OpenAI implementation behind the interface, installed through an optional dependency group rather than required by the deterministic core.
  • llm.fake: deterministic fake responses for tests.
  • llm.schemas: Pydantic structured response models for Topic, Term, and Variable decisions.
  • llm.prompts: prompt builders that delimit untrusted article text and list candidates.
  • classification.candidates: convert vocabulary records into prompt candidates with application-supplied candidate IDs.
  • classification.routers: Topic and Term routers using the model interface.
  • classification.traversal: controlled variable-level descent and branch stopping.
  • classification.validator: deterministic vocabulary validation.
  • classification.redundancy: duplicate and ancestor/descendant final-result removal.
  • pipeline.service: classify a single article.
  • pipeline.batch: process all valid articles, continue after failures, and call persistence incrementally.
  • persistence.cache: compute cache keys from article fingerprint, vocabulary hash, model config, prompt versions, thresholds, and app config.
  • persistence.json_store: atomic incremental JSON writes and result loading.
  • ui.gradio_app: lightweight demonstration UI, installed through an optional dependency group rather than required by the deterministic core.

9. End-to-End Data Flow

data/gcmd_hierarchy.json
        ↓
VocabularyLoader
        ↓
VocabularyIndex with file hash
        ↓
data/articles.json
        ↓
ArticleLoader and schema validation
        ↓
BatchRunner
        ↓
Per article:
  TopicRouter selects zero or more UUID-bearing Topic candidate IDs from the vocabulary index
        ↓
  For each selected Topic:
    TermRouter selects Terms or stops at Topic
        ↓
  For each selected Term/Variable parent:
    Variable traversal evaluates direct children only
        ↓
  Branch stops at deepest supported UUID-bearing node
        ↓
  Application constructs authoritative classification records
        ↓
  Redundancy removal
        ↓
  Deterministic validation
        ↓
  Article result with classifications or no-classification outcome
        ↓
  Incremental JSON persistence and cache update

10. Canonical Vocabulary Data Model

Canonical concept fields:

  • UUID: authoritative UUID from the source hierarchy.
  • name: authoritative label.
  • level: one of Topic, Term, Variable_Level_1, Variable_Level_2, Variable_Level_3.
  • topic: Topic name for this branch.
  • term: Term name when applicable, otherwise None.
  • path_components: ordered labels excluding the root Category.
  • canonical_path: labels joined with >.
  • parent_uuid: immediate parent UUID when the parent is UUID-bearing, otherwise None.
  • parent_name: immediate parent label when present.
  • child_uuids: direct UUID-bearing child UUIDs.
  • has_children: whether the source node has children.
  • assignable: True for every UUID-bearing node.
  • definition: optional source definition.
  • vocabulary_version: file hash unless a future source version exists.

Index structures:

  • by_uuid: dict[str, ConceptRecord]
  • by_path: dict[str, ConceptRecord]
  • parent_by_uuid: dict[str, str | None]
  • children_by_uuid: dict[str | None, list[str]]
  • topic_uuids: list[str]
  • terms_by_topic_uuid: dict[str, list[str]]
  • variables_by_parent_uuid: dict[str, list[str]]

Validation concerns:

  • Duplicate UUIDs are errors.
  • Missing level or name on any node is an error.
  • Missing UUID is allowed only for the known non-assignable root Category, unless an approved vocabulary rule explicitly permits another case.
  • Child levels must follow the GCMD order.
  • Same concept names in different branches are allowed when their canonical paths differ.
  • The same canonical path associated with different UUIDs is an error unless explicitly permitted by an approved vocabulary rule.

11. Article Data Model

Source article fields are preserved exactly:

  • DOI: str
  • Title: str
  • Year: int
  • Abstract: str

Generated article result fields:

  • processing_status
  • classification_outcome
  • classifications
  • no_classification_reason
  • review_status
  • warnings
  • errors
  • processing_metadata

Validation rules:

  • Each record must be an object.
  • Required fields must be present.
  • DOI, Title, and Abstract must be non-empty strings.
  • Year must be an integer.
  • DOI values must be unique.
  • Invalid records are reported explicitly and do not block remaining records.

12. Structured Model Response Schemas

Common candidate decision item:

  • candidate_id: str
  • confidence: float | None
  • evidence: str
  • support_type: Literal["explicit", "inferred", "mixed"] | None
  • reason: str | None

Topic response:

  • selected: list[CandidateDecision]
  • ambiguous_alternatives: list[str]
  • no_selection_reason: str | None

Term response:

  • selected: list[CandidateDecision]
  • stop_at_parent: bool
  • stop_reason: str | None
  • ambiguous_alternatives: list[str]

Variable response:

  • selected: list[CandidateDecision]
  • stop_at_parent: bool
  • stop_reason: str | None
  • ambiguous_alternatives: list[str]

Rules:

  • The model selects only candidate IDs supplied in the prompt.
  • Unknown candidate IDs cause explicit validation errors or retry.
  • stop_at_parent: true requires an empty selected list.
  • A non-empty selected list requires stop_at_parent: false.
  • Contradictory structured responses fail schema validation or trigger retry according to the retry policy.
  • Model confidence is optional, uncalibrated metadata. It cannot independently establish scientific support, justify descent, or cause acceptance without candidate validation, evidence, and final deterministic vocabulary validation.
  • Free-form prose is not parsed for critical decisions.
  • Prompts must delimit title and abstract and instruct the model to ignore commands inside article text.

13. Configuration Design

Configuration sources:

  • Environment variables.
  • Optional .env for local development.
  • Typed defaults in config.py.

Settings:

  • GCMD_HIERARCHY_PATH
  • ARTICLES_PATH
  • OUTPUT_PATH
  • CACHE_PATH
  • MODEL_PROVIDER
  • MODEL_NAME
  • MODEL_TEMPERATURE
  • MODEL_TIMEOUT_SECONDS
  • MODEL_MAX_RETRIES
  • PROMPT_VERSION_TOPIC
  • PROMPT_VERSION_TERM
  • PROMPT_VERSION_VARIABLE
  • MAX_CANDIDATES_PER_PROMPT
  • ENABLE_CACHE
  • FORCE_REPROCESS
  • APP_VERSION
  • LOG_LEVEL

Credentials:

  • API keys are read from environment variables only.
  • .env.example should document names but contain no secrets.

14. Error-Handling Strategy

Error principles:

  • Do not silently convert failures into successful classifications.
  • Failure of one article must not stop the batch.
  • Retries must not duplicate accepted results.
  • Errors must be structured with stage, code, message, and retry count when applicable.
  • Stage-level candidate validation and final deterministic vocabulary validation are separate. Stage-level validation checks model response shape, known candidate IDs, and parent-scoped candidate membership before traversal continues. Final deterministic vocabulary validation checks the constructed final classification record against the canonical vocabulary index before it can be accepted.

Explicit error categories:

  • Malformed hierarchy node.
  • Duplicate UUID.
  • Invalid hierarchy level or parent-child level transition.
  • Malformed article record.
  • Duplicate DOI.
  • Structured response validation failure.
  • Invalid model-selected candidate ID.
  • Model timeout, rate limit, or provider error.
  • Deterministic validation failure.
  • Output schema failure.
  • Persistence failure.

Processing status policy:

  • completed: workflow completed, including valid no-classification.
  • partial: some required branch failed after other branch work completed.
  • failed: article could not be processed.
  • skipped: explicit operator choice only, not cache reuse.
  • Review-related enum values may remain schema-compatible for future use, but the initial MVP must not emit pending_review or review_required without an explicitly approved MVP rule.

15. Persistence and Caching Strategy

Outputs:

  • outputs/classification_results.json
  • outputs/classification_diagnostics.json
  • outputs/run_summary.json
  • outputs/cache/

Incremental persistence:

  • Save an article result immediately after processing completes or fails.
  • Use temporary file plus atomic replace for JSON writes; the temporary file and destination must be on the same filesystem for atomic replace guarantees.
  • Consider an append-safe internal checkpoint representation for crash recovery before producing the final consolidated JSON output.
  • Preserve source article order when practical.
  • Retain errors and warnings per article.

Cache identity includes:

  • DOI.
  • Fingerprint of exact DOI, Title, Year, and Abstract.
  • Vocabulary file hash.
  • Model provider, model name, and parameters.
  • Prompt versions.
  • Relevant thresholds and candidate limits.
  • Application version and configuration hash.

Cache behavior:

  • A cache hit returns processing_status: completed with cache_used: true.
  • Cache reuse is not skipped.
  • Force reprocessing should be configurable.

16. Hugging Face and Gradio Integration Strategy

The Gradio interface should be implemented in src/gcmd_classifier/ui/gradio_app.py after core services work.

UI responsibilities:

  • Accept title and abstract.
  • Call the single-article pipeline service.
  • Display final classifications, UUIDs, canonical paths, confidence signals, evidence, stopping reasons, validation status, and no-classification reasons.
  • Avoid embedding classification logic.

Root app.py timing:

  • Introduce root-level app.py only in the Gradio/Hugging Face milestone.
  • It should be a thin launcher that imports create_demo() or equivalent from gcmd_classifier.ui.gradio_app.

Hugging Face compatibility:

  • Use environment variables for credentials and configuration.
  • Avoid local absolute paths.
  • Keep generated outputs under configurable writable paths.
  • Keep business logic under src/gcmd_classifier/.
  • Preserve prototype/app_hf_poc.py as the baseline.

17. Milestone Sequence

  1. Repository setup and tooling.
  2. Hierarchy loader and vocabulary index.
  3. Article loading and validation.
  4. Shared models, output schema draft, and structured response schemas.
  5. Model abstraction, prompts, fake model, and retries.
  6. Topic routing.
  7. Term routing.
  8. Variable-level controlled descent.
  9. Deterministic validation and redundancy removal.
  10. Batch runner, incremental persistence, caching, and logging.
  11. Gradio integration and Hugging Face launcher.
  12. MVP smoke/evaluation harness.

18. Tasks Within Each Milestone

Milestone 1: Repository Setup and Tooling

Objective: establish installable, testable project scaffolding without classifier behavior.

Included requirements: AGENTS.md / Required Application Components, AGENTS.md / Code Quality, PROJECT_SPEC.md / Non-Functional Requirements / Maintainability and Modularity.

Tasks:

  • Create pyproject.toml with package metadata, Python version, deterministic-core dependencies, pytest configuration, and Ruff configuration.
  • Include Pydantic, pytest, and Ruff in the initial tooling set.
  • Define OpenAI and Gradio as optional dependency groups rather than deterministic-core requirements.
  • Defer choosing mypy versus Pyright.
  • Create package skeleton under src/gcmd_classifier/.
  • Create .env.example.
  • Create .gitignore.
  • Create MVP_PROGRESS.md.
  • Add minimal import tests.

Likely files: pyproject.toml, .env.example, .gitignore, MVP_PROGRESS.md, src/gcmd_classifier/__init__.py, tests/test_package_import.py.

Tests: python -m pytest; import smoke test for gcmd_classifier.

Acceptance criteria:

  • Package imports from src.
  • Test command succeeds.
  • Ruff configuration is present.
  • Type-checker choice remains deferred.
  • No source JSON files modified.
  • No prototype changes.

Excluded work: vocabulary parsing, model calls, UI.

Milestone 2: Hierarchy Loader and Vocabulary Index

Objective: create deterministic canonical vocabulary records and lookups.

Included requirements: PROJECT_SPEC.md / Dataset Specification / GCMD Keyword Dataset, PROJECT_SPEC.md / Functional Requirements / Load the GCMD hierarchy, Create canonical GCMD records, Create hierarchy lookup structures, AGENTS.md / Vocabulary Authority, Assignable Concepts.

Tasks:

  • Implement recursive traversal of variable-depth hierarchy.
  • Calculate vocabulary file hash.
  • Build canonical records for all UUID-bearing nodes.
  • Preserve definitions.
  • Build UUID, path, parent, child, Topic, and Term lookups.
  • Validate duplicate UUIDs, required fields, and level transitions.
  • Add small fixture hierarchy covering Topic through Variable_Level_3, internal assignable nodes, leaf nodes, missing definitions, and malformed records.

Likely files: src/gcmd_classifier/models.py, src/gcmd_classifier/errors.py, src/gcmd_classifier/vocabulary/loader.py, src/gcmd_classifier/vocabulary/index.py, tests/fixtures/gcmd_hierarchy_small.json, tests/test_vocabulary.py.

Tests:

  • Recursive traversal.
  • UUID preservation.
  • Duplicate UUID detection.
  • Canonical path construction.
  • Parent and direct-child lookup.
  • Topic-to-Term and Term-to-Variable lookup.
  • Assignable internal nodes.
  • Optional definitions.
  • Malformed nodes and invalid level transitions.
  • Source-file immutability check.

Acceptance criteria:

  • Current full-data smoke test confirms data/gcmd_hierarchy.json loads into 3,535 UUID-bearing records.
  • Root Category is not assignable.
  • All UUID-bearing internal and leaf nodes are assignable.
  • Tests pass without model access.
  • For the current EARTH SCIENCE vocabulary, every Topic and Term must contain a UUID.

Excluded work: article loading and classification.

Milestone 3: Article Loading and Validation

Objective: load and validate articles.json while preserving source fields exactly.

Included requirements: PROJECT_SPEC.md / Dataset Specification / Article Dataset, PROJECT_SPEC.md / Functional Requirements / Load and validate article records, Preserve article source data, AGENTS.md / Article Data Rules.

Tasks:

  • Implement article model preserving exact source field names.
  • Validate required keys, strict field types, non-empty DOI and Title strings, and DOI uniqueness. Abstract is required and must be a string, but may be empty.
  • Report invalid records without modifying source records.
  • Add valid and invalid article fixtures.

Likely files: src/gcmd_classifier/articles/loader.py, tests/fixtures/articles_valid.json, tests/fixtures/articles_invalid.json, tests/test_articles.py.

Tests:

  • Valid article loading.
  • Missing required fields.
  • Empty DOI and Title. Empty Abstract is allowed and must be preserved exactly.
  • Non-integer Year, including Boolean values.
  • Duplicate DOI.
  • Source values preserved exactly.

Acceptance criteria:

  • Current full-data smoke test confirms data/articles.json contains 468 source records, 467 records with valid non-empty unique DOI values, exactly one invalid record at index 467 due to empty DOI, and 14 otherwise valid records with empty Abstract values.
  • Invalid fixtures produce explicit errors.
  • No normalization or rewriting occurs.

Excluded work: model prompts and batch classification.

Milestone 4: Shared Models, Output Schema Draft, and Structured Response Schemas

Objective: define typed contracts for model responses and result output.

Included requirements: PROJECT_SPEC.md / Functional Requirements / Generate structured model responses, Output Specification, Status Scope, Naming Conventions, AGENTS.md / Structured Model Responses, Output Rules.

Tasks:

  • Define Pydantic response schemas for Topic, Term, and Variable decisions.
  • Define Pydantic result models for article results, classification records, validation result, warnings, errors, and processing metadata.
  • Draft JSON Schemas for classification result and run summary.
  • Validate status enumerations and confidence ranges.

Likely files: src/gcmd_classifier/llm/schemas.py, schemas/classification_result.schema.json, schemas/run_summary.schema.json, tests/test_structured_responses.py, tests/test_output_schema.py.

Tests:

  • Valid structured responses.
  • Unknown/malformed fields rejected according to policy.
  • Confidence range validation.
  • Status scope validation.
  • Minimal no-classification output validation.

Acceptance criteria:

  • Schemas can represent classified, pending-review, failed, partial, and not-classified results.
  • Critical model decisions are structured, not prose-parsed.

Excluded work: live model provider and semantic validator implementation.

Milestone 5: Model Abstraction, Prompts, Fake Model, and Retries

Objective: isolate model calls behind a testable interface.

Included requirements: PROJECT_SPEC.md / System Architecture / Model Abstraction Layer, Structured Model Interface, AGENTS.md / Model Abstraction, Article Data Rules.

Tasks:

  • Define model client protocol/interface.
  • Implement fake model for deterministic tests.
  • Implement optional OpenAI provider behind interface.
  • Implement prompt builders for Topic, Term, and Variable decisions.
  • Delimit article text and include prompt-injection instruction.
  • Add retry wrapper for temporary model failures.
  • Return usage/cost metadata when available.

Likely files: src/gcmd_classifier/llm/base.py, src/gcmd_classifier/llm/fake.py, src/gcmd_classifier/llm/openai_provider.py, src/gcmd_classifier/llm/prompts.py, src/gcmd_classifier/config.py, tests/test_structured_responses.py.

Tests:

  • Fake model returns typed responses.
  • Invalid candidate IDs and contradictory stop/select responses are rejected.
  • Prompt includes candidates and delimited article content.
  • Retry behavior for temporary failures.
  • No API calls in unit tests.

Acceptance criteria:

  • Classification components can depend on an interface, not a provider.
  • Model name and parameters are configurable.

Excluded work: end-to-end classification and unmarked live tests.

Milestone 6: Topic Routing

Objective: select zero, one, or multiple valid Topics from the loaded vocabulary.

Included requirements: PROJECT_SPEC.md / Functional Requirements / Identify relevant GCMD Topics, MVP Classification Workflow / Select Relevant Topics, AGENTS.md / Candidate Selection Rules.

Tasks:

  • Build Topic candidates from the vocabulary index.
  • Route article title/abstract through model interface.
  • Validate selected candidate IDs.
  • Produce branch seed records with evidence/confidence.
  • Support valid no-topic/no-classification outcome.

Likely files: src/gcmd_classifier/classification/candidates.py, src/gcmd_classifier/classification/routers.py, tests/test_pipeline.py.

Tests:

  • Single Topic selection.
  • Multi-Topic selection.
  • No Topic selection.
  • Invalid candidate ID retry/error.
  • Topic UUID/name/path populated from index.

Acceptance criteria:

  • Topic router never accepts non-vocabulary Topics.
  • Topic routing can return no classification without failure.

Excluded work: Term routing and variable descent.

Milestone 7: Term Routing

Objective: evaluate only direct Terms beneath each selected Topic.

Included requirements: PROJECT_SPEC.md / Functional Requirements / Identify relevant Terms, MVP Classification Workflow / Select Relevant Terms Within Each Topic, AGENTS.md / Assignable Concepts.

Tasks:

  • Build direct Term candidates for selected Topic.
  • Allow one or more Term selections.
  • Allow stop at Topic when supported and no Term is adequately supported.
  • Carry evidence and confidence per branch.
  • Validate Topic-to-Term relationship.

Likely files: src/gcmd_classifier/classification/routers.py, src/gcmd_classifier/classification/traversal.py, tests/test_pipeline.py.

Tests:

  • Term selected only from selected Topic.
  • Stop at Topic.
  • Multiple Terms within one Topic.
  • Topic with no supported Term.
  • Invalid Term candidate ID rejected.

Acceptance criteria:

  • Term router never evaluates all Terms globally.
  • Topic-level final classification is possible when Topic has UUID.

Excluded work: Variable descent.

Milestone 8: Variable-Level Controlled Descent

Objective: classify through Variable levels using only direct children and stop at supported assignable parent.

Included requirements: PROJECT_SPEC.md / Functional Requirements / Evaluate Variable-level concepts, Apply controlled hierarchical descent, Prioritize accuracy over specificity, Support multiple classifications, AGENTS.md / Core Classification Rule.

Tasks:

  • Implement reusable recursive/iterative variable descent.
  • Evaluate direct children for Term or Variable parent.
  • Support multiple independent child branches.
  • Give every selected child its own independent branch with branch_id or equivalent provenance.
  • Ensure stopping or failure in one branch does not stop sibling branches.
  • Stop at Term or Variable parent when descent is unsupported.
  • Stop naturally at leaf nodes.
  • Track confidence/evidence by stage.

Likely files: src/gcmd_classifier/classification/traversal.py, tests/test_pipeline.py.

Tests:

  • Stop at Term.
  • Stop at Variable_Level_1.
  • Stop at Variable_Level_2.
  • Select Variable_Level_3.
  • Multiple independent Variable branches.
  • Sibling branch continuation after one branch stops or fails.
  • Leaf-node handling.
  • No forced leaf descent.

Acceptance criteria:

  • No descendant is evaluated unless it is a direct child of the current parent.
  • Branches return deepest supported selected UUID-bearing concept with branch provenance.

Excluded work: Redundancy cleanup and final deterministic validation.

Milestone 9: Deterministic Validation and Redundancy Removal

Objective: ensure final results are structurally valid and non-redundant.

Included requirements: PROJECT_SPEC.md / Functional Requirements / Perform deterministic vocabulary validation, Support multiple classifications, AGENTS.md / Deterministic Validation, Redundancy Rules.

Tasks:

  • Validate UUID existence, name, level, canonical path, parent-child chain, Topic membership, Term membership, and assignability.
  • Remove duplicate UUIDs and duplicate paths.
  • Remove redundant ancestors using UUID ancestry and branch provenance when a descendant in the same branch is retained.
  • Preserve ancestor classifications only when branch provenance shows they represent an independent classification decision.
  • Retain diagnostic information for rejected invalid candidates.

Likely files: src/gcmd_classifier/classification/validator.py, src/gcmd_classifier/classification/redundancy.py, tests/test_validation.py, tests/test_redundancy.py.

Tests:

  • Valid candidate accepted.
  • Nonexistent UUID rejected.
  • UUID/name mismatch rejected.
  • Wrong hierarchy level rejected.
  • Invalid canonical path rejected.
  • Invalid parent-child relationship rejected.
  • Wrong Topic or Term membership rejected.
  • Duplicate and ancestor-descendant redundancy removal using UUID ancestry and branch provenance.

Acceptance criteria:

  • Accepted classifications all pass final deterministic vocabulary validation.
  • Rejected invalid candidates do not appear in final classifications.

Excluded work: independent semantic validation.

Milestone 10: Batch Runner, Incremental Persistence, Caching, and Logging

Objective: process article collections safely and preserve completed results.

Included requirements: PROJECT_SPEC.md / Functional Requirements / Support batch processing, Save results incrementally, Support caching and controlled reprocessing, Record errors and diagnostics, Produce machine-readable output, AGENTS.md / Persistence and Caching, Logging, Error Handling.

Tasks:

  • Implement single-article pipeline service.
  • Implement batch runner over valid article records.
  • Save article-level results incrementally.
  • Evaluate append-safe checkpoint storage for internal recovery before final JSON emission.
  • Continue after article and branch failures.
  • Implement cache key and cache reuse.
  • Emit run summary and diagnostics.
  • Add structured logging without secrets.

Likely files: src/gcmd_classifier/pipeline/service.py, src/gcmd_classifier/pipeline/batch.py, src/gcmd_classifier/persistence/cache.py, src/gcmd_classifier/persistence/json_store.py, src/gcmd_classifier/logging_config.py, tests/test_cache.py, tests/test_pipeline.py.

Tests:

  • Batch continues after one article failure.
  • Incremental or checkpointed result survives simulated later failure.
  • Cache hit sets cache_used: true and remains completed.
  • Cache key changes when article, vocabulary hash, model config, prompt version, or config changes.
  • Output schema validation.
  • No-classification result is completed with empty classifications.

Acceptance criteria:

  • Representative batch can run with fake model and persist valid JSON.
  • Source JSON files remain unchanged.

Excluded work: Gradio UI and live model smoke unless explicitly configured.

Milestone 11: Gradio Integration and Hugging Face Launcher

Objective: provide lightweight demonstration UI without moving classification logic into UI.

Included requirements: PROJECT_SPEC.md / MVP User Interface, AGENTS.md / Hugging Face Compatibility, Proof-of-Concept Application.

Tasks:

  • Implement create_demo() in UI package.
  • Display selected classifications, paths, evidence, validation status, errors, and no-classification reason.
  • Add root-level thin app.py.
  • Confirm prototype remains unchanged.

Likely files: src/gcmd_classifier/ui/gradio_app.py, app.py, UI import test.

Tests:

  • UI module imports.
  • create_demo() constructs Gradio app.
  • Root app.py imports launcher only.

Acceptance criteria:

  • UI calls pipeline service.
  • Business logic remains under src/gcmd_classifier/.
  • Hugging Face launch path is clear.

Excluded work: production review UI.

Milestone 12: MVP Smoke/Evaluation Harness

Objective: provide a small repeatable harness for fake-model and optional live-model checks.

Included requirements: PROJECT_SPEC.md / MVP Evaluation, Evaluation Plan, MVP Evaluation Baseline, AGENTS.md / MVP Evaluation Baseline.

Tasks:

  • Add smoke command or script for a small subset.
  • Add optional integration marker for live model checks.
  • Record basic metrics: article count, classifications, validation failures, model calls, processing time.
  • Preserve prototype baseline for future comparison.

Likely files: smoke tests and optional script only if justified during implementation.

Tests:

  • Fake-model smoke run.
  • Optional live integration test marked and skipped by default.

Acceptance criteria:

  • Smoke run proves the end-to-end path without live API calls.
  • No accepted result fails deterministic validation.

Excluded work: full expert-reviewed evaluation dashboard.

19. Dependencies Between Tasks

  • Milestone 1 precedes all implementation.
  • Milestone 2 is required before candidates, validation, routing, and cache vocabulary identity.
  • Milestone 3 is required before article-level pipeline and batch processing.
  • Milestone 4 is required before model abstraction and output validation.
  • Milestone 5 is required before model-backed routers.
  • Milestone 6 is required before Term routing.
  • Milestone 7 is required before Variable descent.
  • Milestone 8 is required before final candidate validation and redundancy cleanup.
  • Milestone 9 is required before accepted output can be trusted.
  • Milestone 10 is required before UI and smoke testing are meaningful.
  • Milestone 11 depends on the single-article pipeline service.
  • Milestone 12 depends on the batch runner and deterministic validation.

20. Acceptance Criteria for Every Milestone

Global acceptance criteria:

  • Only the approved milestone scope is implemented.
  • Automated tests for new deterministic behavior are added.
  • python -m pytest passes, or failures are reported honestly.
  • Source JSON files remain unchanged.
  • prototype/app_hf_poc.py remains unchanged.
  • MVP_PROGRESS.md is updated during implementation milestones.
  • No live model API call is required for unit tests.
  • Completion report covers files changed, requirements implemented, design decisions, tests, formatting/static checks, source JSON status, deviations, limitations, deferred work, and acceptance status.

Milestone-specific acceptance criteria are listed in section 18.

21. Unit-Test Strategy

Unit tests should cover deterministic behavior without live APIs:

  • Recursive hierarchy traversal.
  • Variable-depth branches.
  • Topic, Term, and Variable-level records.
  • UUID-bearing internal nodes as assignable.
  • Leaf nodes.
  • UUID preservation and duplicate UUID detection.
  • Canonical path construction.
  • Parent, direct-child, Topic-to-Term, and Term-to-Variable lookup.
  • Optional definitions.
  • Malformed hierarchy records.
  • Article schema validation and DOI uniqueness.
  • Structured response validation.
  • Invalid candidate IDs.
  • Deterministic vocabulary validation.
  • Redundancy removal.
  • No-classification behavior.
  • Status handling.
  • Caching behavior.
  • Incremental persistence.
  • Output schema validation.
  • Source-file immutability.

22. Integration-Test Strategy

Integration tests should use model fakes by default:

  • Load small fixture vocabulary and articles.
  • Run Topic -> Term -> Variable descent with scripted fake responses.
  • Validate output JSON schema.
  • Simulate model failures and retries.
  • Simulate invalid model candidate IDs.
  • Simulate partial branch failure.
  • Verify cache reuse.

Optional live tests:

  • Mark with pytest.mark.integration.
  • Skip unless credentials and explicit environment flag are present.
  • Process one or two articles only.
  • Assert structural validity, not scientific correctness.

23. Smoke-Test Strategy

Smoke tests:

  • python -m pytest
  • Import package and create settings.
  • Load full data/gcmd_hierarchy.json.
  • Load full data/articles.json.
  • Run fake-model batch on two fixture articles.
  • Optionally launch/import Gradio app after the UI milestone.

The first full-data smoke should occur after Milestone 3 for loaders and after Milestone 10 for the end-to-end fake pipeline.

24. Test Fixtures and Model Fakes

Fixtures:

  • gcmd_hierarchy_small.json: root Category, two Topics, Terms, variable-depth branches through Variable_Level_3, an internal assignable node, a leaf node, definitions, and sibling branches.
  • Malformed hierarchy fixtures created inline in tests: duplicate UUID, missing name, bad level transition, duplicate canonical path if applicable.
  • articles_valid.json: at least three valid articles, including single-topic, multi-topic, and no-classification cases.
  • articles_invalid.json: missing DOI, empty Title, non-integer Year, duplicate DOI.

Model fakes:

  • Scripted fake selecting known candidate IDs by stage.
  • Fake returning no selections.
  • Fake returning invalid candidate IDs.
  • Fake raising timeout once then succeeding.
  • Fake exhausting retries.
  • Fake returning malformed structured data.

25. Risks and Mitigations

  • Risk: Topic omission blocks correct descendants. Mitigation: Topic prompts favor recall while requiring evidence; evaluate Topic recall separately.
  • Risk: Model over-descends. Mitigation: explicit stopping option at every UUID-bearing level; confidence/evidence required; deterministic direct-child constraints.
  • Risk: Candidate sets exceed prompt limits. Mitigation: configure candidate limit and log exceedance; defer retrieval until measured need.
  • Risk: GCMD labels are ambiguous without definitions. Mitigation: preserve definitions and make optional prompt inclusion configurable.
  • Risk: Model returns invalid candidate IDs. Mitigation: structured validation, retries, and explicit errors.
  • Risk: Cache becomes stale. Mitigation: include article fingerprint, vocabulary hash, model parameters, prompt versions, thresholds, app version, and config hash.
  • Risk: UI logic drifts from batch logic. Mitigation: UI calls the same pipeline service.
  • Risk: Full project semantic validation conflicts with MVP deferral. Mitigation: include future-compatible fields/hooks but do not implement independent validator until approved.
  • Risk: No Git repository means change tracking may rely on file inspection. Mitigation: report files modified carefully at each milestone.

26. Requirement-to-Task Traceability Matrix

Specification requirement MVP task Milestone Test or verification method Status
PROJECT_SPEC.md / Minimum Viable Product / MVP Goals / load and recursively parse gcmd_hierarchy.json Implement recursive hierarchy loader 2 tests/test_vocabulary.py full and fixture loads planned
PROJECT_SPEC.md / Functional Requirements / Create canonical GCMD records Build ConceptRecord and canonical paths 2 Canonical record and path tests planned
PROJECT_SPEC.md / Functional Requirements / Create hierarchy lookup structures Build UUID/path/parent/child/topic/term lookups 2 Lookup tests planned
PROJECT_SPEC.md / Dataset Specification / Assignable Concepts Mark UUID-bearing internal and leaf nodes assignable 2 Internal-node and leaf-node tests planned
PROJECT_SPEC.md / Dataset Specification / Vocabulary Authority and Integrity Detect malformed nodes and duplicate UUIDs 2 Error fixture tests planned
PROJECT_SPEC.md / Functional Requirements / Load and validate article records Implement article loader 3 Article validation tests planned
PROJECT_SPEC.md / Functional Requirements / Preserve article source data Preserve exact source fields 3 Round-trip source-value tests planned
PROJECT_SPEC.md / Functional Requirements / Identify relevant GCMD Topics Implement Topic router 6 Fake-model Topic routing tests planned
PROJECT_SPEC.md / Functional Requirements / Identify relevant Terms Implement Term router using direct children 7 Term routing and wrong-parent tests planned
PROJECT_SPEC.md / Functional Requirements / Evaluate Variable-level concepts Implement reusable variable descent 8 Variable-level traversal tests planned
PROJECT_SPEC.md / Functional Requirements / Apply controlled hierarchical descent Stop at parent when child support is absent 8 Stop-at-each-level tests planned
PROJECT_SPEC.md / Functional Requirements / Prioritize accuracy over specificity Require stop decisions and evidence at each level 8 Fake over-descent and stop tests planned
PROJECT_SPEC.md / Functional Requirements / Support multiple classifications Branch for multiple Topics/Terms/Variables 6-8 Multi-branch fake tests planned
PROJECT_SPEC.md / Functional Requirements / Generate structured model responses Define Pydantic response schemas 4-5 Structured response validation tests planned
PROJECT_SPEC.md / System Architecture / Model Abstraction Layer Implement provider-neutral model interface 5 Fake provider tests planned
PROJECT_SPEC.md / Functional Requirements / Perform deterministic vocabulary validation Implement deterministic validator 9 Positive/negative validation tests planned
PROJECT_SPEC.md / Classification and Validation Workflow / Remove Redundant Candidates Implement redundancy removal 9 Duplicate and ancestor-descendant tests planned
PROJECT_SPEC.md / Classification and Validation Workflow / Handle No-Classification Outcomes Return completed not-classified result 6, 10 No-classification output schema test planned
PROJECT_SPEC.md / Functional Requirements / Support batch processing Implement batch runner 10 Batch fake-model integration tests planned
PROJECT_SPEC.md / Functional Requirements / Save results incrementally Implement JSON store with per-article saves 10 Simulated interruption test planned
PROJECT_SPEC.md / Functional Requirements / Support caching and controlled reprocessing Implement cache identity and reuse 10 Cache hit/miss tests planned
PROJECT_SPEC.md / Functional Requirements / Record errors and diagnostics Structured warnings/errors and diagnostics 10 Failure-path tests planned
PROJECT_SPEC.md / Functional Requirements / Produce machine-readable output Emit JSON result and run summary schemas 4, 10 JSON Schema validation tests planned
PROJECT_SPEC.md / Non-Functional Requirements / Security and Configuration Management Use env config and redact secrets 1, 5, 10 Config tests and log review planned
PROJECT_SPEC.md / MVP User Interface Implement Gradio app calling services 11 UI import/create test planned
AGENTS.md / Hugging Face Compatibility Add thin root app.py launcher 11 Root launcher inspection/import test planned
AGENTS.md / Proof-of-Concept Application Preserve frozen prototype all File unchanged verification planned
PROJECT_SPEC.md / MVP Evaluation Add fake smoke/evaluation harness 12 Smoke command planned

27. Decisions Requiring Human Approval

  • Approve this implementation plan before any implementation begins.
  • Confirm whether Milestone 1 should add or update README setup instructions.
  • Confirm preferred dependency set in pyproject.toml: deterministic core should include Pydantic, pytest, and Ruff; OpenAI and Gradio should be optional groups; mypy versus Pyright remains deferred.
  • Confirm the initial model provider and environment variable names for live use.
  • Confirm whether independent semantic validation remains deferred for MVP, as directed by AGENTS, despite full-project references in PROJECT_SPEC.md.
  • Confirm whether root app.py should wait until Milestone 11.
  • Confirm output directory names and whether generated outputs should be gitignored once a repository exists.

28. Recommended First Implementation Milestone

Recommended first milestone: Milestone 1, repository setup and tooling.

Exact files/modules to create:

  • pyproject.toml
  • .env.example
  • .gitignore
  • MVP_PROGRESS.md
  • src/gcmd_classifier/__init__.py
  • tests/test_package_import.py

Exact behavior:

  • Make the project installable/importable from src.
  • Define pytest and Ruff configuration.
  • Include Pydantic as a deterministic-core dependency.
  • Define OpenAI and Gradio as optional dependency groups.
  • Document expected environment variables without secrets.
  • Establish a progress log for milestone completion reports.

Exact tests to add:

  • A minimal import test, tests/test_package_import.py.

Exact commands that should pass:

python -m pytest

Work that must not begin yet:

  • Do not parse gcmd_hierarchy.json.
  • Do not load articles.json.
  • Do not create model prompts.
  • Do not choose mypy versus Pyright.
  • Do not implement routers or traversal.
  • Do not create root app.py.
  • Do not modify prototype/app_hf_poc.py.

29. MVP Definition of Done

The MVP is done when:

  • Full hierarchy loading creates canonical records and lookups from data/gcmd_hierarchy.json.
  • Articles load from data/articles.json with DOI uniqueness and source preservation.
  • Topic, Term, and Variable classification use only supplied candidates and valid direct children.
  • Classification can stop at Topic, Term, Variable_Level_1, Variable_Level_2, or Variable_Level_3.
  • Multiple independent classifications per article are supported.
  • No-classification is represented as completed processing with empty classifications; the initial MVP does not emit pending_review or review_required without an approved rule.
  • Every accepted classification passes deterministic validation.
  • Duplicate paths and redundant ancestor classifications are removed.
  • Batch processing saves results incrementally and continues after failures.
  • Cache reuse is correct and marked as cache_used: true.
  • JSON output validates against project schemas.
  • Unit and fake integration tests pass without live model access.
  • Lightweight Gradio demo calls application services.
  • Root app.py is a thin launcher.
  • data/gcmd_hierarchy.json, data/articles.json, and prototype/app_hf_poc.py remain unchanged.

Final Review Section

Unresolved Assumptions

  • The initial MVP will not implement a separate independent semantic validator; this follows AGENTS.md / Deferred Features over the broader full-project architecture.
  • The root Category is context only and excluded from canonical assignable output because it has no UUID.
  • Direct-child candidate sets are small enough for default prompts in the initial MVP based on current data inspection.
  • A simple orchestrator is preferable to LangGraph for the MVP.
  • pyproject.toml can be introduced in Milestone 1 because it is absent.
  • The repository may not be initialized as Git in the current environment.

Decisions Requiring Approval

  • Approve or revise the milestone sequence.
  • Approve deferring independent semantic validation.
  • Approve avoiding LangGraph for the MVP implementation.
  • Approve adding pyproject.toml, .env.example, .gitignore, MVP_PROGRESS.md, and import tests in the first milestone.
  • Approve waiting to add root app.py until Gradio integration.

Deviations From PROJECT_SPEC.md or AGENTS.md

  • No deviations from AGENTS.md.
  • The plan intentionally defers independent semantic validation from the full PROJECT_SPEC.md architecture because AGENTS.md explicitly lists it as deferred for the initial MVP. The implementation should keep future-compatible interfaces where practical.

Recommended First Codex Implementation Prompt

Implement Milestone 1 from IMPLEMENTATION_PLAN.md only.

Create the repository setup and tooling files needed for an installable, testable Python package:
- pyproject.toml
- .env.example
- .gitignore
- MVP_PROGRESS.md
- src/gcmd_classifier/__init__.py
- tests/test_package_import.py

Do not implement vocabulary loading, article loading, model integration, classification, persistence, UI, or root app.py.
Do not modify data/gcmd_hierarchy.json, data/articles.json, or prototype/app_hf_poc.py.
Run python -m pytest and update MVP_PROGRESS.md with the milestone result.

Plan Approval Checklist

  • The plan follows AGENTS.md source-of-truth precedence.
  • The MVP uses progressive direct-child classification, not Topic-wide complete-path selection.
  • The prototype remains frozen.
  • The first milestone is small and deterministic, with OpenAI and Gradio kept as optional dependency groups.
  • The proposed package structure keeps UI, persistence, model interaction, vocabulary logic, and classification logic separate.
  • LangGraph is not required for the MVP unless later evidence justifies it.
  • Independent semantic validation is deferred unless explicitly approved.
  • The root app.py is introduced only as a thin launcher during the UI milestone.
  • Tests do not require live model API calls by default.
  • Source JSON files remain read-only.