Skip to content

A coach with a client was told there was nothing to do - #156

Merged
github-actions[bot] merged 1 commit into
masterfrom
feat/job-integration-evidence-v2
Sep 2, 2026
Merged

A coach with a client was told there was nothing to do#156
github-actions[bot] merged 1 commit into
masterfrom
feat/job-integration-evidence-v2

Conversation

@catomean

@catomean catomean commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

feat(jobcoach): a coach with a client was told there was nothing to do

Observed in production on 2026-09-02. Simon B. (Jobcoach) had one client
assigned — George B, created that morning: job-seeking, two years without
work, an unrecognised trade qualification, German at A2. His dashboard said
"Alles unter Kontrolle! Keine dringenden Aufgaben" and never mentioned him.

openTaskCount was criticalIncidents + overdueCheckIns + unplacedResidents +
pendingTransfers + proposalsAwaitingStaff + problemUnits. Every term is a
HOUSING queue, and a Jobcoach holds none of those permissions — so the count
was structurally zero, the state was always quiet, and the screen always
congratulated them.

Same failure the unassigned state was added to fix, one level deeper. Then
it was "nobody is assigned to me". Now it is "somebody is, with real work
outstanding, and the queue is written in another domain's vocabulary".

The fix had to be evidence-shaped rather than invented, because the job side
had NO documented scientific basis at all while housing has had algorithm-docs
since the beginning. That asymmetry is the deeper problem: placement and
employment are the two things AOZ is measured on, and only one of them could
explain itself.

config/job-integration-docs.ts carries the evidence, Swiss and German first
because institutions do not transfer even where mechanisms do:
Integrationsagenda Schweiz (the binding Wirkungsziele AOZ operates under), IAB
on lock-in effects and on refugee employment trajectories, IPS supported
employment, OECD on parallel language and work, SBFI/SEM on recognition and
de-skilling.

Each principle declares whether the product ACTS on it. Three raise work;
three raise nothing on purpose — recognition, post-start support and stated
job goals matter and are not detectable from data this product holds.
Inventing a signal from data that does not exist would be worse than the gap,
and an evidence page implying more than the software does is worse than none.

The queue is pure, with now injected: no contact past a grace period, a
course running with nothing alongside it, a record untouched for six weeks.
The thresholds are named constants carrying their reasoning, because they are
numbers to argue with — 42 days and not 14, since a language course
legitimately runs for months and a queue that fires fortnightly is one a coach
learns to dismiss.

Tiles name the CLIENTS. A bare count would reproduce the original failure of
never mentioning George.

Structure: EvidenceStrength and ResearchSource move to config/evidence.ts so
the second domain does not import the housing algorithm's module for a shared
type; algorithm-docs re-exports them, so no import path changes. Signal copy
lives in the config SSOT and the queue keeps no opinion about rendering. The
caseload query is scoped to the coach's own JOB seat and only runs for a
viewer holding learning:write.

A test pins that every signal principle has an implementation and every
signal has a principle — the guard against evidence-as-decoration, in both
directions.

verify exit 0 (unpiped): 206 suites, 3624 passed. Build exit 0.

Rebased onto the Drizzle migration (#154), which landed mid-flight. The pure modules needed no change — only the caseload query was ORM-shaped.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Cd183M6472xBgTKWA2is6h

Observed in production on 2026-09-02. Simon B. (Jobcoach) had one client
assigned — George B, created that morning: job-seeking, two years without
work, an unrecognised trade qualification, German at A2. His dashboard said
"Alles unter Kontrolle! Keine dringenden Aufgaben" and never mentioned him.

`openTaskCount` was criticalIncidents + overdueCheckIns + unplacedResidents +
pendingTransfers + proposalsAwaitingStaff + problemUnits. Every term is a
HOUSING queue, and a Jobcoach holds none of those permissions — so the count
was structurally zero, the state was always `quiet`, and the screen always
congratulated them.

Same failure the `unassigned` state was added to fix, one level deeper. Then
it was "nobody is assigned to me". Now it is "somebody is, with real work
outstanding, and the queue is written in another domain's vocabulary".

The fix had to be evidence-shaped rather than invented, because the job side
had NO documented scientific basis at all while housing has had algorithm-docs
since the beginning. That asymmetry is the deeper problem: placement and
employment are the two things AOZ is measured on, and only one of them could
explain itself.

`config/job-integration-docs.ts` carries the evidence, Swiss and German first
because institutions do not transfer even where mechanisms do:
Integrationsagenda Schweiz (the binding Wirkungsziele AOZ operates under), IAB
on lock-in effects and on refugee employment trajectories, IPS supported
employment, OECD on parallel language and work, SBFI/SEM on recognition and
de-skilling.

Each principle declares whether the product ACTS on it. Three raise work;
three raise nothing on purpose — recognition, post-start support and stated
job goals matter and are not detectable from data this product holds.
Inventing a signal from data that does not exist would be worse than the gap,
and an evidence page implying more than the software does is worse than none.

The queue is pure, with `now` injected: no contact past a grace period, a
course running with nothing alongside it, a record untouched for six weeks.
The thresholds are named constants carrying their reasoning, because they are
numbers to argue with — 42 days and not 14, since a language course
legitimately runs for months and a queue that fires fortnightly is one a coach
learns to dismiss.

Tiles name the CLIENTS. A bare count would reproduce the original failure of
never mentioning George.

Structure: EvidenceStrength and ResearchSource move to config/evidence.ts so
the second domain does not import the housing algorithm's module for a shared
type; algorithm-docs re-exports them, so no import path changes. Signal copy
lives in the config SSOT and the queue keeps no opinion about rendering. The
caseload query is scoped to the coach's own JOB seat and only runs for a
viewer holding `learning:write`.

A test pins that every `signal` principle has an implementation and every
signal has a principle — the guard against evidence-as-decoration, in both
directions.

verify exit 0 (unpiped): 206 suites, 3624 passed. Build exit 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cd183M6472xBgTKWA2is6h
@github-actions
github-actions Bot merged commit 5bbefb1 into master Sep 2, 2026
4 checks passed
@github-actions
github-actions Bot deleted the feat/job-integration-evidence-v2 branch September 2, 2026 12:07
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