Skip to content

IPO-006..010: factor scoring, dashboard, screener orchestration, enrichment, and the fail-closed AI extraction agent - #108

Merged
DoRmAmMu1997 merged 30 commits into
mainfrom
feat/ipo-006-010-screener-agent
Jul 26, 2026
Merged

IPO-006..010: factor scoring, dashboard, screener orchestration, enrichment, and the fail-closed AI extraction agent#108
DoRmAmMu1997 merged 30 commits into
mainfrom
feat/ipo-006-010-screener-agent

Conversation

@DoRmAmMu1997

@DoRmAmMu1997 DoRmAmMu1997 commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

Delivers the already-combined IPO-006 through IPO-010 scope: deterministic
factor scoring and binary verdicts, the read-only dashboard, idempotent
orchestration, advisory SerpAPI enrichment, and contained AI-assisted PDF
extraction proposals behind human review.

Repository-convention waiver: this PR intentionally keeps IPO-006 through
IPO-010 together because the implementation was already delivered as one
dependent branch before remediation. This is an explicit exception to the
normal one-ticket-per-PR rule.

The central invariant is unchanged: only approved official/manual evidence
can enter scoring
. AI output is a pending proposal, and web results are
quarantined advisory observations.

Post-review hardening

  • Runs PDF parsing in a short-lived, killable worker with wall, page, table,
    row, column, cell, glyph, text, serialization, and Linux address-space
    budgets. The dependency-light backend/ipo_pdf_worker.py target applies the
    512 MiB Linux limit before importing pdfplumber.
  • Requires citation-bound v2 financial facts: exact finite Decimal, semantic
    field/peer label, fiscal period header, cited unit, document SHA, page, and
    table cell/text span. Exact cited narrative evidence is required for objects
    of issue; submission and approval both re-resolve receipts from verified
    cached bytes.
  • Makes proposal approval atomic, detects stale source bytes, enforces one
    pending proposal and semantic proposal/evaluation uniqueness in the
    database, retains reviewed proposal history, and adds --force-extract
    without allowing duplicate pending or identical proposals.
  • Applies per-item enrichment quarantine, typed/versioned advisory authority,
    negation-aware red-flag observations, same-field/same-clause GMP parsing,
    canonical semantic deduplication, and streamed 1 MiB/2,000-character provider
    bounds. Web evidence cannot independently create a hard veto.
  • Scores one immutable semantic evidence snapshot, persists exactly seven
    contribution rows whose sum is the score, closes concurrent evaluation
    races, and exposes source/freshness/staleness on the dashboard.
  • Uses proposition-aware cited debt-purpose evidence; negated, prohibited,
    conflicting, ambiguous, missing, or legacy text cannot clear the high-debt
    caution.
  • Neutralizes CommonMark controls at every untrusted IPO Streamlit
    label/caption/warning/error/expander sink without enabling unsafe HTML.

Documentation pass

  • Audited all 41 Python files changed by the post-review hardening commits.
  • Added or expanded beginner-friendly Google-style docstrings and detailed
    inline rationale in 24 Python files. The remaining touched modules and
    tests already had explanatory module notes or explicit regression-contract
    documentation, so they were reviewed without unnecessary churn.
  • Explains process containment, citation binding, evidence authority,
    transaction/session ownership, semantic idempotency, deterministic scoring,
    migration safety, test intent, and no-network/no-unsafe-Markdown UI
    boundaries next to the code that enforces them.
  • Corrects the repository documentation for the database-enforced
    one-pending-proposal invariant.
  • Syntax-tree comparisons with docstrings removed confirm both documentation
    commits change documentation only; they do not alter executable Python
    behavior.

Migrations

  • 20260713ipo006_screener_artifacts.py introduces the IPO-006 evaluation,
    proposal, and enrichment artifacts.
  • 20260718ipo010_hardening.py adds versioned evidence/breakdown fields,
    pending-proposal and semantic-fingerprint uniqueness, non-null evaluation
    fingerprint uniqueness, retention-safe reviewed proposal references, and
    enrichment semantic first/last-seen identity.
  • Alembic upgrade-to-head parity passes against Base.metadata; policy
    fixtures and hardcoded table/index expectations are updated.
  • No runtime dependency or constraints change.

Security findings addressed

The original review's eight findings and the finalized scan's seven residual
findings are covered by permanent regressions: official/manual authority over
SerpAPI, exact numeric/unit/period/field binding, per-item quarantine,
pre-boundary PDF resource containment, negated debt purpose, same-clause GMP,
semantic enrichment deduplication, provider response bounds, and Markdown-safe
rendering.

The accepted residual risk remains documented: Windows has no standard-library
hard RSS limit, so it uses wall/object/text/result containment; Linux enforces
the 512 MiB child address-space limit.

Verification

  • Documentation-only syntax-tree comparison: 24 Python files verified.
  • Focused IPO/documentation suite: 520 passed, 1 skipped.
  • Local Python 3.13 full suite: 1780 passed, 1 skipped, 89.74% coverage
    (required: 87%).
  • Whole-tree mypy: 245 source files, no issues.
  • Compileall, Ruff, Bandit, all pre-commit hooks, and pip-audit: passed;
    pip-audit reports no known vulnerabilities.
  • Hosted Actions run
    30202559559
    on final head 594583a:
    • Python 3.11 quality and supply-chain checks: passed.
    • Python 3.12 quality and supply-chain checks: passed.
    • Docker image build: passed.

Co-authored by Claude Fable and Codex.

DoRmAmMu1997 and others added 9 commits July 13, 2026 19:49
Pure refactor, behavior identical. The sprint contract names the scoring
modules backend/ipo/scoring/score_model.py and recommendation.py, so move
the IPO-001 scorecard/verdict modules there ahead of the factor-derivation
and caution-flag work:

- git mv backend/ipo/scorecard.py -> backend/ipo/scoring/score_model.py
- git mv backend/ipo/verdict.py   -> backend/ipo/scoring/recommendation.py
- new backend/ipo/scoring/__init__.py package facade
- update the four import sites (backend.ipo facade, domain repository,
  two focused test modules); external callers keep importing score_ipo /
  build_recommendation from backend.ipo unchanged
- update the filename-set assertion in tests/test_ipo_contract_policy.py
- fix module paths in ipo-001 design doc and ipo-screener LLD

Gates: full pytest (1517 passed, coverage 89.55%), ruff, mypy,
compileall all green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Additive migration + ORM for everything the IPO-6..10 screener persists:

- new table ipo_extraction_proposals (IPO-010 review queue): AI-proposed
  extraction payloads with page citations, confidence, verifier notes,
  agent/model provenance, and a fail-closed review lifecycle encoded in
  CHECK constraints (pending rows carry no reviewer; approved rows must
  link the resulting immutable manual-extraction revision)
- new table ipo_enrichment_signals (IPO-009): low-confidence SerpAPI
  observations with query/capture provenance, conservative parsed_value,
  quarantined marker, and a stamped source_policy
- ipo_recommendations: widen ck_ipo_recommendations_type with the fourth
  'Insufficient verified data' verdict type; add caution_flags_json
  (server-default empty list for legacy ipo-001-v1 rows)
- ipo_scores: add nullable inputs_fingerprint (sha-256 of the exact
  evidence scored) as the idempotency anchor for run_ipo_screener
- storage repository helpers for proposals, enrichment signals, and the
  latest-subscription read (SQL stays inside backend/storage)
- downgrade refuses whenever any screener artifact exists, matching the
  ipo002..005 data-preserving pattern

Test co-updates in the same commit per the repo's DB rule: both hardcoded
table-name sets, new index/FK/column assertions, the ipo006 downgrade-
refusal test, and the contract-policy documentation frozensets.

Gates: full pytest (1518 passed, coverage 89.35%), ruff, mypy,
compileall all green; ORM/Alembic parity holds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The factor-derivation layer the IPO-001 design deferred, plus the hard
red-line policy on top of it:

- backend/ipo/scoring/factor_derivation.py: pure derive_score_input()
  mapping ratio receipts + manual-extraction evidence + subscription
  snapshot + (optional) enrichment signals into the seven 0-100
  FactorAssessments. Versioned Decimal band tables (half-open bounds),
  the None-vs-0 rule (missing evidence vs known-weak evidence), peer-
  median valuation premiums, and deterministic evidence-bearing reason
  strings with provenance (formula version, extraction id, sha256).
- backend/ipo/scoring/caution_flags.py: the seven hard caution flags as
  a fixed-order report with TRIGGERED / NOT_TRIGGERED / NOT_EVALUABLE
  outcomes - absent evidence is reported, never guessed. Litigation
  reads only collector-recorded keyword matches from non-quarantined
  web signals.
- backend/ipo/scoring/recommendation.py: new 'Insufficient verified
  data' type on the missing-critical branch; any triggered flag forces
  Not Recommended regardless of score with the flag named first in
  reasons; precedence missing-critical > flags > score bands.
- backend/ipo/models.py: IpoCautionFlag/Report/Status, enrichment
  signal enum + detached record, IpoRecommendationResult.caution_flags
  (+ to_dict).
- repository.evaluate_issue gains caution_flags / inputs_fingerprint /
  model_version keyword args (defaults keep IPO-001 callers identical)
  and round-trips the flag report through caution_flags_json.

Tests first (TDD): tests/test_ipo_factor_derivation.py (every band
boundary, None-vs-0 table, provenance strings), tests/
test_ipo_caution_flags.py (7 flags x 3 outcomes, near-close window,
quarantine discipline), extended verdict + repository + facade tests.

Gates: full pytest (1571 passed, coverage 89.56%), ruff, mypy,
compileall all green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Low-confidence web discovery for the sentiment-only evidence the
deterministic pipeline cannot see:

- backend/ipo/sources/enrichment.py: collect_enrichment_signals runs
  seven fixed query templates (GMP, news, promoter reputation,
  litigation red flags, anchor commentary, brokerage reviews, peer
  discovery) through the shared sixty_seven SerpApiClient and persists
  one ipo_enrichment_signals row per type.
- Trust rules are structural: missing SERPAPI_API_KEY degrades to a
  graceful skip (the screener stays fully functional); every snippet is
  prompt-injection scanned BEFORE storage and a hit is replaced by the
  blocked-evidence marker with quarantined=True; red-flag evidence is
  recorded as matched keywords only (never snippet text); conservative
  GMP parsing requires an explicit GMP mention and stores NULL rather
  than guessing; confidence and source_policy are stamped on every row.
- Per-type failure isolation: one failing query records its exception
  type and the rest of the batch still lands.
- Domain repository record/list functions (SQL stays in storage),
  IpoEnrichmentSignalData DTO, EVENT_IPO_ENRICHMENT_* events, facade
  exports.

Tests: tests/test_ipo_enrichment.py (no-key skip, injection quarantine
round trip, GMP regex table incl. rupee->percent conversion and the
no-price-band case, red-flag keyword capture, per-type isolation,
typed not-found).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The parse stage IPO-003 deferred, split into two pure, AI-free modules:

- backend/ipo/documents/table_extractor.py: extract_document_pages opens
  one hash-verified cached PDF (lazy pdfplumber import, injectable
  open_pdf seam) and returns 1-based ExtractedPage/ExtractedTable
  receipts - the provenance anchors later page citations are verified
  against. Hostile-content caps bound every dimension a PDF author
  controls (800 pages, 20k chars/page, 20 tables/page, 200 chars/cell);
  structural problems surface as typed IpoDocumentParseError codes
  (unreadable_pdf / page_limit_exceeded / empty_document) so parser
  tracebacks never leak file content. Oversized documents are rejected,
  not truncated, because truncation would invalidate page citations.
- backend/ipo/documents/section_classifier.py: classify_pages assigns
  pages to DRHP/RHP section families via a reviewed anchor catalog
  (plain casefolded substring hits, argmax with catalog-order tie
  break); unmatched pages land in an explicit OTHER bucket and each
  receipt records exactly which anchors matched.

Tests: fakes for the pdfplumber seam (caps, None cells, error codes)
plus one true integration read through real pdfplumber against a
byte-accurate minimal PDF assembled in-test (no binary fixture in the
repo); classifier assignment/tie/OTHER/determinism table.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The claude-agent-sdk extraction agent and the human review queue that
keeps its output fail-closed:

- backend/ipo/agents/financial_extractor.py: propose_extraction runs a
  locked-down SDK loop (permission_mode="dontAsk", setting_sources=[],
  in-process tools only - list_sections / read_section / read_tables)
  over the classified pages of one hash-verified cached PDF. Every
  excerpt the model sees is prompt-injection scanned first (a hit is
  non-retryable and fails the run); the final JSON is parsed against a
  strict pydantic schema (values as decimal strings, extra keys
  rejected) with one bounded retry via parse_with_retry; then the HOST
  independently verifies every citation - pages must exist and every
  number must literally appear on its cited page's text/tables. All
  verified -> high confidence; >=90% + all core values -> medium with
  reviewer notes; less -> fail closed, nothing persisted. Failures
  become typed IpoExtractionErrorReceipt values (batch style).
- Review flow in the domain repository: submit_extraction_proposal
  (shape-validated before storage, one pending per document),
  approve_extraction_proposal (reconstructs the strict manual contract
  from the payload and replays submit_manual_extraction - the reviewer
  attests as entered_by_email and the cached PDF bytes are re-hashed),
  reject_extraction_proposal (attributable, reasoned, redacted). Audit
  events on every review decision.
- ui/ipo_manual_page.py gains the "Review AI extraction proposals"
  section: pending queue, verifier notes, payload inspection, Approve /
  Reject controls wired to the signed-in admin identity.
- EVENT_IPO_EXTRACTION_* observability events; facade exports.

Tests: tests/test_ipo_financial_extractor.py (real pdfplumber pass over
an in-test PDF, citation verification tiers, bounded retry, quarantine
non-retry, duplicate/missing-value/parse-code receipts),
tests/test_ipo_extraction_review.py (approve==manual revision round
trip on a verified cache, double-review guards, reject audit trail),
extended manual-page smoke tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The one-command screener and the shared scoring service both it and the
dashboard call:

- backend/ipo/scoring/service.py: rescore_issue loads the full evidence
  bundle (latest manual profile, on-demand ratios, newest subscription,
  enrichment signals), runs derive -> flags -> score -> verdict, and
  persists an immutable ipo-006-v1 evaluation ONLY when the SHA-256
  inputs fingerprint changed. The fingerprint hashes evidence identities
  and rule versions plus two time-DERIVED facts (the set of GMP signals
  still inside the staleness window, and the near-close demand window)
  instead of the clock itself, so re-runs are idempotent until time
  actually changes a factor or flag. Issues without a verified profile
  report insufficient_inputs and write nothing - missing data never
  becomes a fabricated score.
- backend/jobs/run_ipo_screener.py (python -m backend.jobs.run_ipo_screener):
  scan -> download -> enrich -> (--extract only) draft AI proposals ->
  score, mirroring the scan_ipo_filings template: full DI, per-unit
  failure isolation, [ipo-screener] key=value summary grammar
  (recommended / not_recommended with flags= / insufficient_data with
  missing= / totals), frozen outcome dataclasses with an exit_code
  contract. Missing SERPAPI_API_KEY is one graceful skipped_no_key line,
  never a failure; AI extraction stays behind --extract so schedulers
  and CI never spend model credit by accident.
- IpoEvaluationRecord gains inputs_fingerprint; domain readers
  get_latest_evaluation / get_latest_subscription; screener events;
  facade + scoring-package exports (service deliberately NOT re-exported
  from backend.ipo.scoring to avoid an import cycle with the repository).

Tests: tests/test_ipo_scoring_service.py (real file-backed DB round
trip: evaluated -> skipped_unchanged -> price-band/subscription/GMP
changes re-open the fingerprint; clock-independence of the fingerprint),
tests/test_run_ipo_screener_job.py (stage gating, --extract targeting,
isolation + exit codes, no-key skip, summary grammar, CLI wiring).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The read-only surface over everything the pipeline persists:

- backend/ipo/dashboard.py (Streamlit-free): build_dashboard_snapshot
  denormalizes every issue's stored state - latest evaluation, manual
  profile, cached documents, pending proposals - into display-ready
  rows; pure section selectors implement the spec's seven sections
  (Available filings / Open / Upcoming / DRHP watchlist / Recommended /
  Not Recommended / Missing data queue); top_positive_and_risk_reasons
  ranks the stored contribution receipts against PDF_WEIGHTS (missing
  factors are never labeled risks - "could not check" and "checked and
  weak" stay distinct messages). IpoEvaluationRecord now restores the
  contributions receipt for this.
- ui/ipo_page.py: _render_ipo_page(can_rescore, user_email) renders the
  sections, the binary verdict filter, and per-issue score-breakdown
  expanders (reasons, hard flags, missing data, source documents). The
  four stored recommendation_type strings map onto the sprint's friendly
  labels purely in the UI. The capability-gated "Re-score all issues"
  button runs the same repository-only scoring service the job uses,
  audits the outcome counts, and invalidates the 5-minute snapshot
  cache. Zero network inside render.
- app.py: "IPO screener" joins the base view list for every
  authenticated user (same tier as Validation); the dispatch passes
  can_rescore=MANAGE_IPO_DATA. Orchestration tests updated (nav tuples,
  re-export identity, keyword-only capability boundary).

Tests: tests/test_ipo_dashboard_builder.py (strength/risk selection,
section membership, missing-data queue rules, snapshot denormalization
over monkeypatched repositories), tests/test_app_ipo_page.py (label-map
completeness against the 4 DB strings, filter semantics, spec column
contract, render-touches-no-repository smoke, re-score audit + cache
invalidation + failure isolation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ticket-scoped design docs for every sprint in this change, in the
repo's decision-doc style:

- ipo-006-factor-derivation-and-verdict.md: band tables, the
  None-vs-zero rule, the seven-flag catalog with its three outcomes,
  and the verdict precedence (missing-critical > triggered flags >
  score bands) behind the new 'Insufficient verified data' type.
- ipo-007-dashboard.md: builder/renderer split, section rules,
  strengths/risks selection, and the capability-gated re-score action.
- ipo-008-screener-orchestration.md: stage pipeline, failure and
  configuration semantics, summary grammar, and the inputs-fingerprint
  idempotency contract (time-derived facts, not the clock).
- ipo-009-serpapi-enrichment.md: the structural trust rules for
  low-confidence web signals.
- ipo-010-ai-extraction-proposals.md: the three trust tiers of
  automated extraction and the proposal/review model; deliberate
  deferrals (parse_status vocabulary, OCR).
- components/ipo-extraction-ai.md: new LLD mirroring fundamentals-ai.md
  for the fourth Claude Agent SDK agent.
- components/ipo-screener.md: status now IPO-001..010; new section
  mapping each addition to its design doc; extension points updated.
- docs/architecture/README.md index entries; AGENTS.md repo-map row,
  design-doc index, and the run_ipo_screener command.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@DoRmAmMu1997 DoRmAmMu1997 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Self-review: code review + security review (author-run, per AGENTS.md workflow)

Code review — verdict: no blocking findings

Verified beyond the gate suite (1640 tests, 89.9% coverage, ruff/mypy/compileall/bandit/pip-audit all green):

  • Verdict precedence (missing-critical > triggered flags > score bands) and the binary invariant are pinned by tests, including a triggered flag overriding a 95-point score.
  • Number-verification normalization exercised directly against edge inputs: 1,234.50 with commas/₹/Rs noise matches, parenthesised negatives (56.70) match -56.70, rounded variants match, absent numbers reject. Whitespace-stripping can merge adjacent digit runs, so a false acceptance is theoretically possible for a value that already appears on the page — the fail-closed direction is unaffected and human review remains the gate.
  • Import-cycle hazard between scoring.service and the repository is resolved structurally (service deliberately not re-exported from backend.ipo.scoring; comment documents why).
  • Fingerprint idempotency hashes time-derived facts (usable GMP set, near-close window) rather than the clock; clock-independence is pinned by a test.
  • Known accepted nits: dashboard builder does per-issue reads (documented; universe is dozens of issues, page cached); the entirely_ofs_weak_growth evidence line reads slightly oddly for a zero-fresh/zero-OFS issue (status is still correct); approve/approve race leaves one extra append-only revision and fails loudly (documented in the docstring and error message).

Security review — verdict: no critical/high findings; residual risks documented

  • SSRF: no new arbitrary-URL fetches. Enrichment reuses the fixed-endpoint SerpAPI client (result links stored as data, never fetched); the extraction agent reads only the hash-verified local cache and never sees a path.
  • Prompt injection (TEST-003): every prospectus excerpt handed to the model and every web snippet stored passes the shared quarantine engine first. Hits are non-retryable (extractor) or replace text with the blocked marker (quarantined=true rows); raw hostile text never reaches storage, logs, or the model.
  • Trust boundaries are structural: AI output persists only as pending proposals scoring never reads; approval re-validates the payload through the strict manual contract and re-hashes the cached PDF bytes; enrichment signals are typed away from every financial-statement field.
  • AuthZ: re-score requires MANAGE_IPO_DATA at the dispatch boundary; proposal review lives inside the admin page with its defense-in-depth re-check; reviewer identity comes from the server session, never the form; review decisions and re-scores are audit-logged.
  • Secret safety: all new persisted JSON passes normalize_secret_safe_json; rejection notes pass redact_text; failure paths carry exception type names and stable codes only.
  • Resource exhaustion: hostile-PDF caps (800 pages / 20k chars / 20 tables / 200-char cells), bounded section chunks, bounded search results, linear regexes.
  • Residual risks (accepted, consistent with the audit register's posture): (1) model-transcribed free text (objects of issue, peer names, verifier notes) is displayed to the reviewing admin — a hostile document could try to social-engineer the reviewer through it; the control is review against the source PDF plus host-verified citations. (2) The digit-merge false-acceptance noted above. Neither creates a path for unverified data to reach scoring.

🤖 Generated with Claude Code

@DoRmAmMu1997 DoRmAmMu1997 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Codex review — changes required before merge

I reviewed the exact PR range de8f199…aab2cd4, including the IPO-006 through IPO-010 implementation, focused runtime reproductions, migration/layering policy, and the rendered dashboard path. The current CI is green, but the review found eight validated security/data-integrity issues:

  1. Medium — SerpAPI authority inversion: uncorroborated litigation snippet text can become a score-independent hard veto, including negated text such as “No litigation…”.
  2. Low — numeric evidence equivalence: rounded values, digit substrings, and values assembled across cells can pass host verification.
  3. Low — uncited unit scale: model-selected units are allowlisted but not bound to cited source evidence before HIGH.
  4. Low — row-wide quarantine fail-open: one hostile result quarantines clean siblings, after which the caution path reports NOT_TRIGGERED.
  5. Low — PDF page limit is post-materialization: the complete page tree is built before the page cap is checked.
  6. Low — PDF table limits are post-extraction/incomplete: all tables are extracted before slicing, with no row/column/total-cell budget.
  7. Low — PDF text limit is post-extraction: the parser constructs the complete text map/string before truncation.
  8. Low — debt-purpose negation: free-text substring matching treats negated repayment wording as affirmative evidence and can suppress the high-debt caution.

The broader correctness pass also found work required around proposal source freshness and atomic approval, database-enforced pending/evaluation uniqueness, reviewed-proposal retention, extraction rerun idempotency, semantic score fingerprints, consistent input snapshots, period ordering, page-safe chunks, continuation-page classification, GMP proximity parsing, dashboard provenance/freshness, factor-impact ordering, Markdown-safe rendering, and stale architecture/operations documentation.

The implementation will keep this already-combined IPO-006…010 PR, with an explicit waiver of the repository’s normal one-ticket-per-PR convention. Fixes will be test-first, preserve official/manual evidence precedence and human approval, and retain the existing deterministic scoring weights and verdict bands.

This is a COMMENT review rather than an approval/request-changes event because the automation identity is also the PR author.

DoRmAmMu1997 and others added 6 commits July 18, 2026 09:35
Document the accepted PDF isolation, cited-evidence, enrichment authority,
atomic review, and semantic idempotency boundaries before implementation.

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>

@DoRmAmMu1997 DoRmAmMu1997 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Codex post-security review — changes still required

The finalized branch-diff scan closed 24/24 coverage rows and validated seven remaining issues against local remediation head 7130cc3a606f0ddb9a2b3e30d5031a4df026737a (the six local remediation commits are not pushed yet):

  • Financial evidence is token-bound but not yet semantically bound to the claimed field, period, and unit context.
  • A page-wide unit fallback can select unrelated text instead of the cited value's own table/text context.
  • Negated debt-repayment language can suppress the high-leverage caution.
  • GMP parsing can consume an unrelated percentage from a neighboring clause.
  • Semantically identical enrichment observations can be persisted repeatedly and overweighted.
  • Untrusted Markdown control characters are not neutralized at every Streamlit rendering boundary.
  • Provider-result object work is not bounded early enough before downstream processing.

These are fixable without changing the intended deterministic weights, recommendation bands, binary verdict, or advisory-only web-evidence policy. I will add failing regression tests first, apply the narrow authority/resource-boundary fixes, rerun the original local checks plus the repository gates, then push and update this review with evidence.

Repository convention waiver: PR #108 intentionally retains IPO-006 through IPO-010 as the already-combined delivery; this is an explicit exception to the normal one-ticket-per-PR rule.

DoRmAmMu1997 and others added 12 commits July 22, 2026 11:48
Translate the approved PR #108 remediation design into five test-first,
review-gated implementation tasks for the seven residual findings and
hosted PR closure.

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>

@DoRmAmMu1997 DoRmAmMu1997 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Codex completion review — ready to merge

I re-reviewed the complete range
de8f199b937a2ec9c7f78d1ed86bf6924841fac4...326061d838dab57e6a17e0ad147582e0b88c44c8
after remediation.

The original eight validated findings and the seven residual post-review
findings are addressed with permanent regression coverage:

  • official/manual evidence authority prevents SerpAPI hard-veto inversion;
  • PDF work is killable and bounded before crossing the worker boundary;
  • financial values, units, periods, field/peer meaning, pages, cells/spans,
    source tokens, and document SHA are atomic v2 evidence;
  • cited narrative evidence and proposition-aware debt-purpose classification
    fail closed on negation, ambiguity, conflict, legacy data, and forgery;
  • proposal approval, retention, uniqueness, force-rerun, and evaluation
    idempotency are transactionally and semantically enforced;
  • per-item quarantine, same-clause GMP, semantic deduplication, advisory
    authority, and streamed provider limits prevent web-evidence escalation;
  • seven-row score provenance, source freshness/staleness, deterministic impact
    ordering, and Markdown-safe Streamlit rendering are public and tested.

The first hosted run exposed a Linux-only spawn bootstrap problem: importing
the broad backend.ipo facade consumed address space before the 512 MiB child
limit. Commit 326061d moves the target to dependency-light
backend/ipo_pdf_worker.py, preserving the limit and allowing pdfplumber to
start. The rerun is fully green:

  • Python 3.11 quality and supply-chain checks: passed.
  • Python 3.12 quality and supply-chain checks: passed.
  • Docker image build: passed.
  • Focused IPO suite: 464 passed, 1 skipped.
  • Exact cross-finding selection: 68 passed.
  • Migration parity: 13 passed.
  • Whole-tree mypy: 245 source files, no issues.
  • Ruff, compileall, Bandit, pre-commit config, and pip-audit: passed.

The PR description now includes the explicit IPO-006…010 combined-ticket
waiver, both migration notes, security closure, residual Windows memory-limit
risk, and verification evidence. The live PR is MERGEABLE / CLEAN, all
checks pass, and there are no inline review threads.

This is a COMMENT review because the automation identity is also the PR
author and cannot self-approve.

Add beginner-friendly docstrings and inline comments throughout the IPO-006
through IPO-010 implementation. Explain the safety boundaries, evidence
authority, transaction ownership, scoring semantics, and UI rendering rules
without changing runtime behavior.

Co-authored-by: Codex <codex@openai.com>
@DoRmAmMu1997

DoRmAmMu1997 commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

Documentation pass completed on final head 594583a.

  • Audited all 41 Python files touched by the post-review hardening commits.
  • Added or expanded beginner-friendly docstrings and inline rationale in 24
    files; the other touched modules and tests already contained explanatory
    module or regression-contract documentation.
  • Covered the PDF worker boundary, typed/cited evidence verification,
    enrichment authority, transaction ownership, semantic idempotency,
    deterministic scoring, migration invariants, test intent, and Streamlit
    safety rules.
  • Verified both commits are documentation-only by comparing Python syntax
    trees after removing docstrings.
  • Local: 1780 passed, 1 skipped, 89.74% coverage; all pre-commit hooks, Ruff,
    mypy, compileall, Bandit, and pip-audit passed.
  • Hosted run 30202559559: Python 3.11, Python 3.12, and Docker all passed.

No review threads remain unresolved, and the PR is mergeable.

Expand the repository, scorecard, and verdict test module notes so beginners
can understand the persistence, fail-closed scoring, and public JSON contracts
that the scenarios protect.

Co-authored-by: Codex <codex@openai.com>
@DoRmAmMu1997
DoRmAmMu1997 merged commit 62403cb into main Jul 26, 2026
3 checks passed
@DoRmAmMu1997
DoRmAmMu1997 deleted the feat/ipo-006-010-screener-agent branch July 26, 2026 12:52
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