Skip to content

Keep rows with placeholder cells, report row removals, guard bad-cell filters - #21

Merged
DHYEY166 merged 1 commit into
mainfrom
fix/executor-row-loss-and-sidecars
Oct 3, 2026
Merged

DHYEY166 merged 1 commit into
mainfrom
fix/executor-row-loss-and-sidecars

Conversation

@DHYEY166

@DHYEY166 DHYEY166 commented Oct 3, 2026

Copy link
Copy Markdown
Owner

Fixes the issues from a small xlsx run where every suggested rule was approved: the Brown row (. in its three numeric cells) disappeared, three numeric columns got orig_<col> copies in the deliverable, and the COLLEGEPOND REFERENCE label row was gone. Adds a deterministic guard for row-removing AI suggestions.

Root causes

  1. Brown: no type cast drops rows (type_cast turns . into null and keeps the row). Brown could only be removed by a row filter on a ranking column. Two paths in the old code: a neq "." filter (the prompt told the model to "filter to exclude" sentinels), or a comparison filter (gt 0), which treated . and nulls as failing the comparison. Nothing recorded how many rows a rule removed, so the loss was silent.
  2. orig_<col> columns: trim_whitespace turned numbers into strings (34 became "34"), so the later cast wrote __orig_ sidecars for every value. column_header_normalize then snake-cased __orig_X to orig_x, so split_deliverable no longer saw them as sidecars and they leaked into output.xlsx.
  3. COLLEGEPOND REFERENCE: not a header-detection bug. Both the executor and the profiler read it as the first data row (tests pin this). The suggested rules with no value filters keep it, so it was removed by an approved rule that targeted rows by value. It is the same class of problem as 1, and the new per-rule rows_removed makes it visible.

Changes

Executor (lambdas/executor/handler.py)

  • rows_removed per rule goes into _execution and the log.
  • type_cast accepts null_values (placeholder codes become null and the row is kept).
  • Comparison filters keep rows whose value is missing or unparseable.
  • trim_whitespace strips only text.
  • Sidecars are kept only when a value lost information. rename and column_header_normalize move the sidecar with its column.
  • Bad-cell guard (bad_cell_rows()): counts the rows a filter/filter_extended/drop_nulls removed only because of one placeholder or non-number cell in an otherwise valid row (at least half of the other cells filled in). This is recorded as _execution.bad_cell_rows. In auto mode such a rule is skipped and the rows are kept. A rule a person approved still runs and is reported.

App

  • src/lib/rule-guard.ts: after the AI returns rules and before they are stored, flagged rules get parameters._guard (reason plus a type_cast/null_values alternative). The rule card shows the warning, "Approve all" skips flagged rules, and auto-validate never approves one.
  • Prompt: a single bullet changed. Sentinels in a mostly numeric column now map to type_cast to null, with a filter only when the whole row is invalid. This is generic, with no dataset-specific tokens.
  • Run page: rows removed per rule and in the summary, plus "Removed for one bad cell" and "Kept by bad-cell guard" counts.

The guard does not flag legitimate filters: comparisons, keep-only matches (eq/in/regex), numeric values, real text values (neq "deleted", not_in ["test"]), blank removal, or mostly empty rows. PLACEHOLDER_TOKENS is shared between TS and Python and checked by a contract test.

Tests

  • lambdas/tests/test_college_list.py: fixture shaped like the real file.
  • lambdas/tests/test_bad_cell_guard.py and src/lib/rule-guard.test.ts: flagged and not-flagged cases.
  • Route tests for suggest-transforms (guard wiring) and auto-validate (flagged rules are rejected).
  • The fake model's rules are unchanged (trim/type_cast/normalize), and integration and e2e pass.

Docs: docs/transform-rules.md.

After merge: redeploy the executor Lambda (zip-swap). The profiler is unchanged.

… filters

Executor
- Record rows_removed (and bad_cell_rows) per rule in _execution and log every removal.
- type_cast accepts null_values: placeholder codes become null, the row is kept.
- Comparison filters (gt/lt/...) keep rows whose value is missing or unparseable.
- trim_whitespace strips only text, so numbers in mixed columns stay numbers.
- __orig_<col> sidecars are kept only when a value lost information; rename and
  column_header_normalize move the sidecar with its column (it used to be
  snake-cased to orig_<col> and leak into the deliverable).
- Bad-cell guard: count rows a filter/drop_nulls removed only because of one
  placeholder or non-number cell in an otherwise valid row; skip such a rule in
  auto mode, where nobody approved it.

App
- suggest-transforms flags such rules (parameters._guard, src/lib/rule-guard.ts)
  before they are shown; the rule card shows the warning and the type_cast
  alternative, "Approve all" skips flagged rules and auto-validate rejects them.
- Prompt: placeholders in mostly numeric columns -> type_cast to null, not filter.
- Run page: rows removed per rule and in the summary, plus bad-cell counts.

Docs: docs/transform-rules.md. Tests: college-list fixture, guard unit tests in
Python and TS (including legitimate filters that are not flagged), contract test
for the shared placeholder list.
@vercel

vercel Bot commented Oct 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
clean-stack Ready Ready Preview Oct 3, 2026 2:09pm UTC

@DHYEY166
DHYEY166 merged commit 9676666 into main Oct 3, 2026
9 checks passed

This branch was successfully deployed

2 active deployments
Preview – cleanstack — 5218c29d Deployed Oct 3, 2026 by vercel[bot]
Preview – clean-stack — 5218c29d Deployed Oct 3, 2026 by vercel[bot]
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