Skip to content

Keep one-letter skills without corrupting others - #50

Open
Disesfgewu wants to merge 5 commits into
sysprog21:mainfrom
Disesfgewu:fix-grounding-short-skills
Open

Disesfgewu wants to merge 5 commits into
sysprog21:mainfrom
Disesfgewu:fix-grounding-short-skills

Conversation

@Disesfgewu

@Disesfgewu Disesfgewu commented Sep 16, 2026

Copy link
Copy Markdown

What

unique() filtered candidates shorter than two characters, meant to drop
stray punctuation left over from a bad split. It also silently dropped
legitimate one-letter skills like C and R before a candidate ever got to
choose them for the interviewer.

Dropping that filter unmasked a second, latent bug: clean()'s leading-strip
regex also swallows a bare digit run, so 5G lost its 5 and survived as the
fabricated skill G once nothing was left to catch the mangled remnant.

Why

  • clean() now only strips a real list markegits
    immediately followed by . or ) and then whitespace -- not a bare leading
    digit. 5G, 3D, 4K, 802.11 pass throow
    Ruststill loses its1. `.
  • unique() now keeps a token only if it hasgit,
    so a split fragment that is pure punctuation (a stray /, #, & left
    over from a bad split) is dropped instead ol.

Testing

scripts/test.sh: 438/438 non-skipped tests
digit-led skills, numbered-list stripping on requirement lines, and
punctuation-only split fragments.


Summary by cubic

Keeps one-letter skills like C and R during grounding extraction instead of dropping every candidate shorter than two characters.

Fixing that exposed a second bug: clean() stripped a bare leading digit run as if it were a list marker, so 5G lost its 5 and survived as the fabricated skill G. clean() now strips only real markers such as 1. or 2) followed by whitespace, so 5G, 3D, and 4K survive while numbered-list stripping on requirement lines still works. unique() now drops pure-punctuation fragments, lone numbers, and ambiguous bare decimals like 3.14; letter-bearing tokens, plain digit runs, and values like 24/7 or 100% still pass.

Written for commit 20931a6. Summary will update on new commits.

Review in cubic

cubic-dev-ai[bot]

This comment was marked as resolved.

Comment thread web/document-grounding.js Outdated
Comment thread web/document-grounding.js Outdated
@Disesfgewu
Disesfgewu requested a review from jserv September 16, 2026 15:31

@jserv jserv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Indent via make indent and then squash commits without Claude markers.

@Disesfgewu
Disesfgewu force-pushed the fix-grounding-short-skills branch 2 times, most recently from 1496214 to 2ad5ddb Compare September 16, 2026 15:43
@Disesfgewu
Disesfgewu requested a review from jserv September 16, 2026 15:45
@Disesfgewu

Copy link
Copy Markdown
Author

Indent via make indent and then squash commits without Claude markers.

Sorry about that! I've run make indent, squashed the commits, and pushed the updates. Could you please take another look when you have a moment? Thanks!

@jserv

jserv commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

I've run make indent, squashed the commits, and pushed the updates. Could you please take another look when you have a moment?

You didn't. Check git manual carefully for git rebase.
Don't be overly polite by including "sorry" in replies; it is not particularly useful during collaboration.

@Disesfgewu

Copy link
Copy Markdown
Author

Oops. I check that again.

@Disesfgewu
Disesfgewu force-pushed the fix-grounding-short-skills branch from 2ad5ddb to 618f25b Compare September 16, 2026 16:07
@jserv

jserv commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Check Git Squash Commits: How to Combine Commits Into One by using git rebase -i carefully.

cubic-dev-ai[bot]

This comment was marked as resolved.

@ColtenOuO ColtenOuO left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It's recommended to make the commit message title relevant to the actual changes, as this helps others quickly understand the updates when reviewing.

@Disesfgewu

Copy link
Copy Markdown
Author

It's recommended to make the commit message title relevant to the actual changes, as this helps others quickly understand the updates when reviewing.

That's very helpful advice. I'll apply this practice to my future commits.

@ColtenOuO ColtenOuO left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As a reminder, once the issue pointed out is fixed, we should add a regression test for it to ensure the bug doesn't come back in future updates.

@Disesfgewu
Disesfgewu force-pushed the fix-grounding-short-skills branch from 618f25b to 5af2df8 Compare September 16, 2026 16:33

@jserv jserv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rebase latest main branch and refine commit messages properly.

@Disesfgewu
Disesfgewu force-pushed the fix-grounding-short-skills branch 2 times, most recently from 1f9eec9 to a1f91ad Compare September 16, 2026 16:44
@jserv
jserv requested a review from ColtenOuO September 16, 2026 16:46
cubic-dev-ai[bot]

This comment was marked as resolved.

Martin and others added 3 commits September 17, 2026 00:49
unique() filtered candidates shorter than two characters to drop stray
punctuation left over from a bad split, but the same filter erased
legitimate one-letter skill names such as C and R before a candidate
ever got to choose them for the interviewer. Drop the length floor and
keep only the empty-string filter it was meant to provide.
Dropping unique()'s length floor to keep one-letter skills also
unmasked a latent bug: clean()'s leading-strip regex swallowed a bare
digit run, so "5G" lost its "5" and survived as the fabricated skill
"G" instead of being dropped like before. Only strip a genuine list
marker -- a bullet glyph, or digits immediately followed by "." or ")"
and then whitespace. Also drop split fragments that clean() leaves as
pure punctuation, since those are leftover delimiters, not skills.
Apply suggestion to handle single digit edge cases and add regular
expressions to cover additional document processing patterns.
@Disesfgewu
Disesfgewu force-pushed the fix-grounding-short-skills branch from a1f91ad to c932cb1 Compare September 16, 2026 16:50
Comment thread web/document-grounding.js Outdated
const jd = await parseGroundingFile(txt("1. Must know Rust\n2) Should know Go"), "jd");
assert.deepEqual(jd.requirements, ["Must know Rust", "Should know Go"]);
});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
test("lone numeric fragments are not kept as skills", async () => {
const resume = await parseGroundingFile(
txt("Skills: Python, 1, 1., Rust"),
"resume",
);
assert.deepEqual(resume.skills, ["Python", "Rust"]);
});

The tests still don't cover the edge case mentioned earlier. Whenever a bug occurs, it highlights a gap in our current test coverage. We should add a test for this specific scenario to prevent regressions in the future.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I would add more tests now to edge cases.

@ColtenOuO

Copy link
Copy Markdown
Collaborator

Also, I don't think it's a good idea to describe the collaboration process in the commit body. It should focus on what was actually changed.

This issue is still present in the current commit message.

Update parsing logic to drop standalone numbers and decimals like 3.14.

Refine inline documentation and add unit tests for edge cases.
cubic-dev-ai[bot]

This comment was marked as resolved.

Comment thread web/document-grounding.js
// front.
function clean(line) {
return line.replace(/^[-*•\d.)\s]+/, "").slice(0, textLimit).trim();
return line.trimStart().replace(/^(?:(?:[-*]|\d+[.)])\s+|•\s*)+/, "").slice(0, textLimit).trim();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Requiring whitespace after the marker breaks list styles that main handled. 1.Must know Rust and 1.) Must know Rust now reach the interviewer with the marker still attached, and so do *Should know Go and -- Must know Rust. Letting a numbered marker be followed by a letter keeps 5G and 3D intact, because the digits still need a . or ) right after them:

Suggested change
return line.trimStart().replace(/^(?:(?:[-*]|\d+[.)])\s+|\s*)+/, "").slice(0, textLimit).trim();
return line.trimStart().replace(/^(?:(?:-+|\*)\s+|\d+[.)]+(?=\s|\p{L})\s*|\s*)+/u, "").slice(0, textLimit).trim();

*Should and hierarchical markers like 1.1 Must are still not stripped. Handling them without also eating *nix or 802.11 experience is worth a follow-up with its own tests.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I would check this later.
Thank for your suggestion. :)

Comment thread web/document-grounding.js
return [...new Set(values.map(clean).filter((value) => value.length >= 2))].slice(0, max);
return [...new Set(values.map(clean).filter((value) => {
if (/\p{L}/u.test(value)) return true;
if (/^\d+(\.\d+)+$/.test(value)) return false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

\d only matches ASCII digits, but the other checks here use \p{N}, so ٣.١٤ and 5.2 skip this check, and the last line then keeps them. The comment above also says 3.14 could come from Python 3.14 being split on whitespace. parseResume only splits on ,, ; and |, so a bare 3.14 can only come from the source text. The PR description says 802.11 passes through, but this rule drops it, and the new test expects that.

Suggested change
if (/^\d+(\.\d+)+$/.test(value)) return false;
if (/^\p{Nd}+(?:\.\p{Nd}+)+$/u.test(value)) return false;

Comment thread web/document-grounding.js
if (/\p{L}/u.test(value)) return true;
if (/^\d+(\.\d+)+$/.test(value)) return false;
if (/\p{N}/u.test(value) && /[/%]/.test(value)) return true;
return /\p{N}.*\p{N}/u.test(value);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

/\p{N}.*\p{N}/u accepts any token with two digits anywhere in it, so -50, (3.14), 3.14., 1-2 and 2020-2024 all become skills. On main, clean() reduced every one of them to an empty string. Worse, Skills: 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, Rust now fills all eight skill slots with years and drops Rust. If the goal is to keep plain digit runs like 27001, match exactly that:

Suggested change
return /\p{N}.*\p{N}/u.test(value);
return /^\p{Nd}{2,}$/u.test(value);


test("a bare multi-digit integer is not treated the same as a digit.digit fragment", async () => {
// Unlike "3.14", a plain digit run has no dot to make it read as a split
// version number or GPA, so it keeps surviving the way it always has.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"keeps surviving the way it always has" is wrong. On main, clean() stripped every leading digit, so 27001, 2015 and 754 all came out empty and were dropped. Keeping bare numbers is new behavior in this PR, so the comment and the PR description should say so. They should also explain why a year like 2015 belongs in a skills list at all.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The meaning of this is that if the skill start from “GPA 3.87” for example. It will keep surviving. If it only “3.87” would be filtered.

For second 2015 in next test. The reason that it could keep surviving is “ISO” text in the front. but it still has a problem that we could not figure out if ISO 2015 is a license or not by regular expression.
So in my opinion to deal with these the only way is using LLM or NLP method to do it but it will cost a lot for token and time latency.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So in my opinion to deal with these the only way is using LLM or NLP method to do it but it will cost a lot for token and time latency.

Use deterministic approaches wherever possible, so we can enforce restrictions through predefined rules.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I think use the deterministic method is more convenient and would check it later.
But so in these case, for example “Skills: ISO 2016, 27087, 202610” something like these input, for deterministic approach would pass due to ISO text here, but we still need to pass the text in real ISO like “ISO 27001”. We still have no way to solve but we use specific way only for this case.
That’s not a great coding way because if do it, we need to cover all possible cases. 🫠

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ISO does not carry over to the following values here. parseResume() splits Skills: ISO 9001, 27001, 2015 into three independent candidates, so 27001 and 2015 are currently accepted only because bare multi-digit strings pass the filter.

The deterministic rule does not need to verify whether ISO 2016 is a real standard. It only needs to require enough context in each candidate: keep a token if it contains a Unicode letter. That accepts ISO 27001, IEEE 754, 802.11ac, 5G, C, and R, while rejecting ambiguous bare values such as 27001, 2015, and 754.

If a document writes ISO 27001, 27002, inferring that the prefix applies to both values would be a separate parsing feature. The current parser does not preserve that relationship, so accepting the second number would be guessing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That’s not a great coding way because if do it, we need to cover all possible cases.

So, consider decoupling this feature in a future pull request. For now, only formatting checks are needed.

@ColtenOuO ColtenOuO left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think some of the newly added tests seem unrelated to this PR (e.g., single isolated skill) and might not be necessary.

While adding tests for different cases can be beneficial, I think we should have a clear rationale for why each change is being made. Just to clarify my earlier point about missing tests: I meant that we still need a test specifically reproducing the scenario where the bug occurred, rather than adding unrelated test cases to this PR.

Adding unrelated tests without a clear purpose might just introduce unnecessary review overhead.

cc. @jserv what do you think?

@ColtenOuO

Copy link
Copy Markdown
Collaborator

By the way, the PR description might need to be updated to reflect the latest changes, as some parts appear to be outdated.

@jserv

jserv commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

I think some of the newly added tests seem unrelated to this PR (e.g., single isolated skill) and might not be necessary.
[...]
Adding unrelated tests without a clear purpose might just introduce unnecessary review overhead.

For small projects like this, it is acceptable to combine diverse changes into a single pull request, provided that each change has a clear purpose and the overall changes are not extensive enough to make the review difficult, allowing reviewers to still walk through them effectively.

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.

3 participants