Skip to content

feat(oferta,auto-pipeline): gate evaluation on a step-0 liveness check#939

Closed
maxmilian wants to merge 1 commit into
santifer:mainfrom
maxmilian:fix-835-eval-liveness-gate
Closed

feat(oferta,auto-pipeline): gate evaluation on a step-0 liveness check#939
maxmilian wants to merge 1 commit into
santifer:mainfrom
maxmilian:fix-835-eval-liveness-gate

Conversation

@maxmilian

@maxmilian maxmilian commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds a step-0 URL liveness gate to oferta and auto-pipeline, so a dead posting is caught before the full A-G evaluation, the report, and the tailored CV are generated — closing the eval-side half of the liveness-gate class.

Related issue

Closes #835

Why

The Level 3 (WebSearch) scan appends hits to pipeline.md as pending entries, and the eval side never re-verifies. oferta / auto-pipeline run all of blocks A-G, write a report, and generate a tailored PDF CV before anyone discovers the URL 404s. The reporter burned a session on 17/25 dead links, including the session's top-scored offer (4.3/5) whose company had rebranded months earlier — full evaluation + PDF generated on phantom data.

scan already gates Level 3 results (step 7.5, scan --verify) and apply gates at its preflight (#887). This was the remaining gap: the eval side had no step-0 check, exactly as the maintainer noted in the issue thread.

What users will see

  • Pasting a URL into oferta or triggering auto-pipeline now verifies the posting is live first. On dead-posting evidence (404/410, "position closed / filled / no longer accepting applications", a hard redirect to a generic careers page, or a page with no JD), it stops before any A-G work — no report, no CV — marks the entry - [x] ~~Company | Role~~ — posting expired in pipeline.md, and tells the user the link is dead.
  • Pasted JD text (no URL) is unaffected.
  • check-liveness.mjs <url> is the documented headless fallback when Playwright MCP isn't available.

Surface area

Two mode files (modes/oferta.md, modes/auto-pipeline.md) + integrity tests in test-all.mjs. System Layer only; no user-layer files. In oferta.md, the new gate takes Step 0 and Archetype Detection moves to Step 1 — Block G's "snapshot already captured in Step 0" now correctly points at the gate (which is where the browser_snapshot is taken).

Screenshots

N/A — mode-instruction (prompt) change.

Bug fix verification

test-all.mjs gains two integrity assertions (mirroring the existing apply-preflight check) so the gate can't silently disappear: oferta must carry ## Step 0 — Liveness gate + stop before Block A + Do not continue to Step 1 until liveness is resolved; auto-pipeline must carry Liveness gate (first) + do not run Step 1 evaluation. Both go red without the mode-file changes and green with them.

Validation

node test-all.mjs --quick231 passed, 0 failed (the 16 warnings are pre-existing santifer.io personal-data false-positives in README.pl/ar/ua + the cv-sync no-user-data check — unrelated).

Note on scope (translations)

Scoped to the two English modes named in the issue. The Arabic modes/ar/fursah.md has the same Step 0 and would benefit from the same gate — happy to extend it here or leave it for a follow-up i18n PR, your call.

Checklist

  • I have read CONTRIBUTING.md
  • I linked a related issue above
  • My PR does not include personal data (CV, email, real names)
  • I ran the test suite (node test-all.mjs --quick) and all tests pass (0 failed)
  • My changes respect the Data Contract (no user-layer files)
  • My changes align with the project roadmap

Summary by CodeRabbit

  • New Features

    • Added a liveness verification check that validates job postings are active before processing. If a posting is expired or dead, the pipeline stops immediately and informs the user.
  • Tests

    • Added test coverage to verify liveness gate functionality is properly implemented and prevents workflow execution for inactive postings.

The Level 3 scan appends WebSearch hits to pipeline.md with no liveness
check, and the eval side never re-verifies: oferta / auto-pipeline run
the full A-G evaluation, write a report, and generate a tailored CV
before anyone discovers the posting 404s. One reporter burned a session
on 17/25 dead links, including a top-scored offer whose company had
rebranded months earlier.

scan (step 7.5) and apply (santifer#887 preflight) already gate on liveness;
this closes the remaining eval-side gap. oferta gets a Step 0 liveness
gate (renumbering Archetype Detection to Step 1, which is also where
Block G's snapshot reference now correctly points), and auto-pipeline's
Step 0 verifies the URL before extraction. On 404/expired/closed
evidence both stop before any LLM work, mark the pipeline entry dead,
and fall back to check-liveness.mjs when Playwright MCP is unavailable.

Closes santifer#835
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds a liveness verification gate at the beginning of the URL-based job evaluation pipeline in oferta.md and auto-pipeline.md to detect and stop processing of dead or expired job postings before any evaluation occurs. Test assertions in test-all.mjs validate that these gates are present and enforce the early stop.

Changes

Liveness Gate for Dead Link Detection

Layer / File(s) Summary
Liveness gate workflow definitions
modes/oferta.md, modes/auto-pipeline.md
oferta.md replaces "Step 0 — Archetype Detection" with a "Step 0 — Liveness gate" applied when the candidate provides a URL; the gate uses Playwright navigation and snapshot to verify the posting is live, stops before "Block A" if expired, and includes fallback manual confirmation and a reference to the check-liveness.mjs script. auto-pipeline.md adds a parallel "Liveness gate (first)" check at the start of URL-based Step 0 extraction to prevent downstream evaluation and PDF generation if the posting is detected as dead, with similar fallback handling. Archetype Detection in oferta.md moves to Step 1.
Liveness gate integrity checks
test-all.mjs
New assertions verify that modes/oferta.md contains the step-0 liveness-gating section and halts before evaluation if liveness is unresolved, and that modes/auto-pipeline.md contains an initial liveness gate preventing Step 1 evaluation; each check reports pass/fail status.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • santifer/career-ops#887: Both PRs introduce a Playwright-based liveness gate before downstream evaluation/generation and extend test-all.mjs to assert the presence of these liveness-blocking mode sections.

Suggested labels

📄 docs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a Step-0 liveness check gate to evaluation in oferta and auto-pipeline modes.
Linked Issues check ✅ Passed The changes directly address all primary coding objectives from issue #835: implementing Step-0 liveness verification in oferta and auto-pipeline modes, stopping evaluation before Block A for dead postings, marking expired entries in pipeline.md, and documenting the fallback check-liveness.mjs script.
Out of Scope Changes check ✅ Passed All changes are strictly scoped to the liveness gate implementation: updates to modes/oferta.md and modes/auto-pipeline.md, integrity test assertions in test-all.mjs, and renumbering of Archetype Detection from Step 0 to Step 1—no extraneous modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown
Contributor

Welcome to career-ops, @maxmilian! Thanks for your first PR.

A few things to know:

  • Tests will run automatically — check the status below
  • Make sure you've linked a related issue (required for features)
  • Read CONTRIBUTING.md if you haven't

We'll review your PR soon. Join our Discord if you have questions.

@maxmilian maxmilian marked this pull request as ready for review June 11, 2026 15:16

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test-all.mjs`:
- Around line 518-533: The current assertions only check substring presence;
update the tests for ofertaMode and autoPipelineMode to also verify ordering by
using index comparisons: ensure ofertaMode.indexOf('Step 0') and/or
ofertaMode.indexOf('Liveness gate') are >= 0 and less than
ofertaMode.indexOf('Step 1') before calling pass/fail, and similarly ensure
autoPipelineMode.indexOf('Liveness gate (first)') and/or
autoPipelineMode.indexOf('do not run Step 1 evaluation') are found and occur
before the indexOf('Step 1') in autoPipelineMode; use the existing variables
ofertaMode and autoPipelineMode and keep the same pass/fail calls but only pass
when both presence and correct order are satisfied.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 85704811-d116-45a2-8c17-59719417b47c

📥 Commits

Reviewing files that changed from the base of the PR and between edc971a and b491c63.

📒 Files selected for processing (3)
  • modes/auto-pipeline.md
  • modes/oferta.md
  • test-all.mjs

Comment thread test-all.mjs
Comment on lines +518 to +533
if (
ofertaMode.includes('## Step 0 — Liveness gate') &&
ofertaMode.includes('stop before Block A') &&
ofertaMode.includes('Do not continue to Step 1 until liveness is resolved')
) {
pass('oferta mode gates evaluation on a step-0 liveness check');
} else {
fail('oferta mode missing the step-0 liveness gate');
}

const autoPipelineMode = readFile('modes/auto-pipeline.md');
if (
autoPipelineMode.includes('Liveness gate (first)') &&
autoPipelineMode.includes('do not run Step 1 evaluation')
) {
pass('auto-pipeline mode gates evaluation on URL liveness');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Strengthen liveness-gate assertions with ordering checks.

These checks only assert substring presence; they can pass even if the gate text is moved below Step 1. Validate relative order (Step 0/Liveness gate appears before Step 1) to enforce the contract.

Suggested hardening
 const ofertaMode = readFile('modes/oferta.md');
+const ofertaStep0 = ofertaMode.indexOf('## Step 0 — Liveness gate');
+const ofertaStep1 = ofertaMode.indexOf('## Step 1 — Archetype Detection');
 if (
-  ofertaMode.includes('## Step 0 — Liveness gate') &&
+  ofertaStep0 !== -1 &&
+  ofertaStep1 !== -1 &&
+  ofertaStep0 < ofertaStep1 &&
   ofertaMode.includes('stop before Block A') &&
   ofertaMode.includes('Do not continue to Step 1 until liveness is resolved')
 ) {
   pass('oferta mode gates evaluation on a step-0 liveness check');
 } else {
   fail('oferta mode missing the step-0 liveness gate');
 }

 const autoPipelineMode = readFile('modes/auto-pipeline.md');
+const autoStep0 = autoPipelineMode.indexOf('## Step 0 — Extract JD');
+const autoStep1 = autoPipelineMode.indexOf('## Step 1 — A-G Evaluation');
+const autoGate = autoPipelineMode.indexOf('Liveness gate (first)');
 if (
-  autoPipelineMode.includes('Liveness gate (first)') &&
+  autoStep0 !== -1 &&
+  autoStep1 !== -1 &&
+  autoGate !== -1 &&
+  autoStep0 <= autoGate &&
+  autoGate < autoStep1 &&
   autoPipelineMode.includes('do not run Step 1 evaluation')
 ) {
   pass('auto-pipeline mode gates evaluation on URL liveness');
 } else {
   fail('auto-pipeline mode missing the liveness gate before evaluation');
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test-all.mjs` around lines 518 - 533, The current assertions only check
substring presence; update the tests for ofertaMode and autoPipelineMode to also
verify ordering by using index comparisons: ensure ofertaMode.indexOf('Step 0')
and/or ofertaMode.indexOf('Liveness gate') are >= 0 and less than
ofertaMode.indexOf('Step 1') before calling pass/fail, and similarly ensure
autoPipelineMode.indexOf('Liveness gate (first)') and/or
autoPipelineMode.indexOf('do not run Step 1 evaluation') are found and occur
before the indexOf('Step 1') in autoPipelineMode; use the existing variables
ofertaMode and autoPipelineMode and keep the same pass/fail calls but only pass
when both presence and correct order are satisfied.

@maxmilian

Copy link
Copy Markdown
Contributor Author

Closing as a duplicate of #937, which addresses the same #835 eval-side liveness gate (opened earlier, same three files) and has already been through review. Consolidating on #937 to avoid splitting reviewer attention. Apologies for the double-open.

@maxmilian maxmilian closed this Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dead links enter pipeline without liveness verification — full eval + PDF generated for expired postings

1 participant