Files
krow_talent_app/scripts/__baseline__
Aravind 34505d7cf6 chore: refresh owliver baseline
Commit B of the baseline remediation, and independent of Commit A.

The baseline recorded 23 skills; the runtime resolves 24. The one addition
is `create-employee-role`, which appears in the skill list and in the two
contexts that offer it, `admin.positions` and `admin.talentPool`. Three
lines, three insertions, no deletions.

The drift is older than either workstream and was never a symptom of them.
`src/skills/owliver/create-employee-role.md` exists on `main` and at
`dca1842`; it was introduced by `6249e00`/`e02a0c2`, while this baseline was
last written at `eaf08e0`/`b2e6868` — before the skill existed. It is absent
from `dca1842`'s file list and from every commit on this branch. The skill
files and the baseline are byte-identical between `main` and HEAD, so `main`
captures the same drift.

Refreshing it cannot hide a migration regression, which is the only reason
it is safe to write. `owliver-capture.mjs` loads seven modules — the skills
registry, `placement`, `owliverResolver`, `dynamic`, `routing`, `insights`
and `seed` — and none of them is among the files whose emitted JavaScript
differs from `main`. Identical inputs, identical capture. What a regression
would look like here is a removal or a moved route, and there is neither:
routes are byte-identical at 18, the eleven contexts are the same eleven
with the same fields, and no skill was lost.

Written with `node scripts/owliver-baseline.mjs --write`, the mechanism the
file's own header names. Verified before the write by capturing to a scratch
path and diffing, and after it by confirming the written file is byte-
identical to that independent capture.

  owliver-baseline.mjs   "Owliver behaviour matches the baseline." — exit 0
  typecheck              0 errors
  lint                   exit 0, 0 errors, 289 warnings
  npm test               1693/1693, exit 0
  build                  exit 0, bundle 74d17e2d… unchanged

Nothing under `src/` changed, no Owliver skill, agent, route or context was
edited, the capture scripts are untouched, and Commit A's five files are
byte-identical to `878a235`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8
2026-09-20 01:00:54 +05:30
..

Owliver behaviour baseline

owliver-baseline.json records how the panel routed and answered before the agent layer existed. skill-check.mjs asserts against it on every run.

Regenerating it is a deliberate act, and the reason belongs here.

2026-09-20 — the three HTML baselines that held invented people were recaptured

Settled. The section that stood here said six checks failed on purpose and listed the evidence; this is that debt being paid.

hiringRecords.js used to pad the hires list with five invented people (DEMO_FILL) so Hired History read as a history rather than as three rows. The padding reached Analytics too, where "total hires" counted eight against a database holding three. It was removed, and because these baselines render with queries disabled, the padding was all they contained. Candidates separately became the final-selection queue, which changed what its empty state says.

Verified before regenerating, so the drift was known rather than assumed. Tag counts for Hired History, baseline -> now:

<td      40 -> 0     five table rows of people who were never hired
<span    51 -> 13
<div     78 -> 39
<p       26 -> 7
<svg     20 -> 8

Every difference was content that had been fabricated. Analytics lost 269 class attributes and 238 words to the same cause. Candidates gained exactly one class — the description paragraph under "Nobody is awaiting a decision". No styling rule, ordering rule or structural rule changed on any of the three.

Recaptured and renamed, because a file called pre-migration that holds post-dca1842 markup is a lie in the filename:

hired-history.pre-migration.html  ->  hired-history.render.html
analytics.pre-migration.html      ->  analytics.render.html
candidates.pre-migration.html     ->  candidates.render.html

activity-page, candidates-analysis, control-center, positions and talent-pool still hold genuine pre-migration markup, still pass, and keep the name that says so. The PAGES table in skill-check.mjs now carries each baseline's FILENAME rather than deriving one suffix for all of them.

The migration proof was not thrown away with the baseline. One check — Hired History added only identity wrappers — compared tag tallies to assert that migrating the page added exactly two <div>s and changed nothing else. That was true, and it was checkable only while the DATA was frozen as well: the predicate subtracts one whole render from another, so removing the invented hires moved every count and the arithmetic stopped describing wrappers at all. Recapturing would not have rescued it — with the baseline equal to the render the delta is zero, and a predicate demanding two can never hold again. Left in place it would have stayed red for a new reason, which is worse than failing for the old one.

It is replaced by three checks that read the render itself and need no frozen file, so they keep holding as the page's content changes:

Hired History wraps exactly the node types registered to wrap
Hired History identities are unique and name composed nodes
Hired History identity wrappers carry no styling

Together these say what the tally said — UiTreeRenderer encloses a type registered wrap: true in <div {...attrs}>, every other type takes the attributes on its own root element, and a wrapper contributes identity and no styling. Hired History composes five nodes; two are registered to wrap and produce the two divs, two carry their identity on their own <section>, and hired-extensions-top is an extension slot that renders nothing while no skill is attached to it. The second check therefore asserts uniqueness and no strays rather than one identity per composed node — a one-to-one rule would be asserting that every extension slot is always filled.

carries node identity in the DOM was described here as needing pre-migration markup. It does not: it reads only the live render, names chronology and records directly, and is unaffected by any of this. It still passes.

npm test is 1693/1693.

2026-08-27 — the seed gained the three statuses nothing exercised

application_status has seven values. The fixture produced four: applied, ai_screened, interview, hired. shortlisted, rejected and assigned existed only in the schema, and Assignment shipped empty by design.

That gap was hiding real defects, all found the same week and none visible against the old data:

  • atOrBeyond ranks a status by its index in STAGE_ORDER, which listed five of the seven. rejected and assigned scored -1 and dropped out of every bucket including applied, so a position's funnel lost people and a role whose candidates had all been assigned read as unfilled.
  • The agent's candidates_awaiting excluded hired and rejected from "still in the running", so somebody already working a shift was offered as a person to chase.
  • insights.stalled counted "screened and waiting on a decision" as status === 'ai_screened' only, silently dropping the shortlisted — the people that phrase most describes. Caught by this regeneration, not before it.

The change: three existing applications were converted, not added, so every regression anchor holds — 24 applications, 9 scored averaging 76, 3 staff, 8 postings, all unchanged.

app_kevin   applied      → rejected      (never scored; a hard requirement)
app_sofia   ai_screened  → shortlisted   (the strongest screened candidate)
app_marco   hired        → assigned      (hired, then rostered)

Plus one Assignment for Marco — the minimum that makes assigned real. Eight of the nine positions still have none, so every reader still meets the empty case.

What drifted, verified before regenerating: one number, in four prompts. Kevin left applied, so the unscored count reads 9 where it read 10:

"Show the 10 applications waiting for review"  →  "…9 applications…"
"10 have no score" / "10 unscored applicants" / "10 unscored applications"

Nothing else moved. 1 waiting on a decision briefly became a fallback prompt and that was the stalled defect above — fixed in insights.js rather than absorbed into the baseline, and the prompt came back on its own.

2026-08-27 — the local simulator was removed

The panel used to answer from two places: an agent, and ~3,300 lines of browser-side templates. Two paths behind one avatar meant the same question got different answers depending on phrasing, so the templates were deleted.

What actually drifted, verified before regenerating: one thing. The "I do not have that on this page" decline used to list the page's capability labels as bullets — those labels were the simulator's canned readings and went with it. It now names the page's topics in a sentence.

doc shape:  ["text","list","note"]  →  ["text","text","note"]

Every intent kind was unchanged. No routing moved, no skill matching changed. That is why this regeneration was safe: the diff was read first, and it was one cosmetic change on a path that only runs when no agent is configured at all.

2026-09-11 — Candidates became the final-selection queue

candidates: shows the same words now fails, and it is the only check this change breaks.

The page used to list every application the org had ever taken — all seven statuses at once — which made it a second Talent Pool rather than the queue of hiring decisions waiting on a human. It now opens on final selection: interview completed, not hired, not rejected. That is a change to what the page says, so a check asserting the page says exactly what it said before was always going to fail. There is no version of this work that leaves those words alone.

What drifted, verified before leaving it failing. The rendered delta is the empty state and nothing else:

before  "…0 of 0 candidates  No candidates match your filters"
now     "…0 of 0 candidates  Nobody is awaiting a decision
         Candidates arrive here once their interview is completed,
         and leave once they are hired or declined."

Title, subtitle and toolbar meta are byte-identical. The old copy was not merely different, it was untrue: with no filters applied there is nothing to clear, and "no candidates match your filters" describes a filter that was never set.

Tag and class counts, baseline → now:

class="…"    26 -> 27     one inserted: the description paragraph

One insertion, nothing changed and nothing dropped — which is why candidates: paints the same styled elements, in the same order and candidates: carries node identity in the DOM both still pass. Those two are the migration proof; only the words moved.

The new stage filter options cost nothing here. FilterSelect is a Radix Select, so its options live in a portal that is closed in static markup, and SelectValue renders empty on the server — the baseline contains neither the old option labels nor the new ones.

Regenerated on 2026-09-20, with the other two — see the entry at the top of this file. It was held until then because candidates.pre-migration.html was load-bearing for the UI node tree proof, and recapturing it earlier would have written post-migration markup into a file named pre-migration. The file is now candidates.render.html and the proof it was holding up has been replaced by three checks that read the render directly.