You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Fifteen console types already disagree with the Worker — a date that never renders, a history that is always empty, no error anywhere #617
Fifteen console types disagree with the Worker today — silent, because the fields are optional on both sides
A subset of #616 that is already wrong in production, not merely unguarded.
Two confirmed by name:
console expects
Worker actually sends
Effect
KnowledgeDoc.createdAt
addedAt
the date never renders
Credential.history
recoveryHistory
the history is always empty
Both fields are optional on both sides, so TypeScript is satisfied, nothing throws, nothing logs, and the value is simply undefined forever. That is the worst failure mode in this class: a feature that looks implemented and quietly does nothing.
Thirteen more were counted in the same sweep and not individually named.
#616 is the systemic fix — derive or compare all 125, ratchet the rest. That is real work with a real cost.
These fifteen are live defects with a user-visible effect right now, and each is a one-line rename. They should not wait on the architecture. Fixing them first also gives #616 a clean baseline: a guard introduced over a surface with fifteen known-bad rows starts life with an exemption list, which is how a ratchet becomes permanent.
Acceptance criteria
All fifteen enumerated with file:line on both sides — the console's expectation and the Worker's producer.
Each resolved by correcting the consumer, unless the Worker's name is genuinely wrong, in which case say so — renaming a producer field is an API change and needs to be stated as one.
For each, note what the user was not seeing. A silently-empty history and a missing date have different weights, and the report should say which mattered.
Inferred: that all fifteen are simple renames. Only two were examined in detail — some of the other thirteen may be structural rather than nominal, and AC1 exists to establish that before anyone estimates the work.
AC1 — the enumeration, and it is seventeen, not fifteen
Denominator: 42 console-side API-response type declarations inspected, 17 diverged. The issue's caveat was right — only 4 of the 17 are nominal renames; 13 are structural, and one of those must not be renamed at all.
#
console (file:line)
field
Worker producer (file:line)
producer sends
class
1
lib/types.ts:183
KnowledgeDoc.createdAt
workers/api/src/agent-types.ts:80, written agent-do-knowledge.ts:80,:245
addedAt
nominal
2
lib/types.ts:182
KnowledgeDoc.type
agent-types.ts:74-82
nothing — provenance is source + sourceUrl
structural
3
lib/types.ts:217
Credential.history
lib/credentials.ts:30, mapped :107
recoveryHistory (col recovery_history)
nominal
4
lib/types.ts:56
Instance.capabilities.boardColumns
routes/instances.ts:315
stripped before reply
structural
5
lib/types.ts:58
Instance.capabilities.settingsSchema
routes/instances.ts:315
stripped before reply
structural
6
lib/types.ts:227
Notification.instanceId
routes/notifications.ts:14 (SELECT *)
col is agent_id, holding an agent id
structural
7
lib/types.ts:235
Notification.createdAt
same
created_at
nominal — already mitigated
8
lib/types.ts:247
Notification.data
same
no such column, ever
structural
9
lib/types.ts:226
Notification.read: boolean
migrations/0006:9
INTEGER → 0/1
structural
10
lib/types.ts:158
RuntimeTask.needs_human: boolean
packages/browser-runner/src/types.ts:14
it is a TaskStatusvalue, not a field
structural
11
lib/types.ts:159
RuntimeTask.handoff_reason
—
nothing, anywhere
structural
12
lib/types.ts:160
RuntimeTask.handoff_field
—
nothing, anywhere
structural
13
lib/types.ts:153
RuntimeTask.result
browser-runner/src/types.ts:44
output + error
structural
14
lib/types.ts:166
RuntimeEvent.timestamp (required)
browser-runner/src/types.ts:57
createdAt only
structural
15
lib/types.ts:14
Agent.config: object
routes/agents.ts:137 (SELECT *)
a JSON string; /:id omits it entirely
structural
16
lib/types.ts:13
Agent.creator_login
agents.ts:137, :267
not a column — only a CASE … as alias in the browse query
structural
17
tabs/IndexingTab.tsx:43
InstanceTrigger.action (4 members)
lib/trigger-types.ts:34
7 members
structural
AC3 — what the user was not seeing, weighted
One had a visible symptom.#1: the doc viewer's "added on" stamp (tabs/KnowledgeTab.tsx:506) has never rendered for anyone. tabs/IndexingTab.tsx:188 had already hit this and worked around it locally with doc as KnowledgeDoc & { addedAt?: string } — which is the evidence that the declaration, not the wire, was wrong.
One was load-bearing and worse than a missing field.#12: RunDetail.tsx:405 read task?.handoff_field || detail.replace(/^Needs your input…/i,"")… || "your answer".
The left operand can never win, so the label on the box a user fills during a handoff has always been scraped out of prose, or degraded to the literal words "your answer". Filed as #621.
Two were dead optimisations.#4/#5: routes/instances.ts:315 strips both fields deliberately (measured: ~83 KB for 28 instances), and my/instances is the only endpoint any consumer of Instance calls. SettingsTab.tsx:58 calls its prop "the fast path" — for an optimisation that has never once fired. The board first-paints GENERIC_COLUMNS; settings first-paints empty. Both self-heal on the next fetch, which is why nobody reported them. Filed as #623.
One corrects this issue's premise.#3 is described here as "the history is always empty". It is not — no component reads either name; the credential card renders domain and username only (KnowledgeTab.tsx:737-738). The cost so far is zero. It is fixed because the wrong name is what the next person to build that UI would reach for.
One must NOT be renamed — AC2's escape hatch, used once.#6: matching the producer would give navigate(/instances/<agent_id>), a broken route rather than a fixed one. The branch was deleted, and the gap it was hiding (a row with no url is unclickable) is filed as #622.
The rest cost nothing yet and were type-lies waiting to be believed.#14 is the sharpest: timestamp was declared required directly above a comment admitting it "is often absent". It is always absent — nothing emits it anywhere — so the declaration promised every future reader a string that never arrives. #17 is notable for a different reason: the console's other copy of that union (TriggersSection.tsx:53) had all seven, so the console disagreed with itself as well as with the Worker. Both now point at one declaration.
AC2 — every fix is on the consumer
No producer was renamed. The Worker's names were correct in all 17 cases; #4/#5 are a deliberate server decision with a measurement attached, and #6 is the case where the producer's name is right and still must not be adopted. So no API change is required or made.
AC4 — a test per fix, watched red
store/console/src/lib/types.test.ts. It is types, not assertions, because this defect class is invisible at runtime by construction — optional on both sides, so nothing throws and nothing logs — and a fixture-driven test is written from the same wrong belief as the code, so it agrees with the bug. Exclude<keyof Console, keyof Producer> extends never compares the declarations instead; store/console/tsconfig.json has include: ["src"], so CI's tsc --noEmit compiles it.
Watched red, each one, before the fix went back:
restoring createdAt/type → types.test.ts(48,7): Type 'true' is not assignable to type 'never', plus three real read sites
restoring handoff_field/needs_human → the same at the RuntimeTask assertion
dropping run_pipeline from the union → the guard plus three live comparison sites in TriggersSection.tsx, which is what proves the dedup actually connected
re-adding instanceId / history → compile error from the exhaustive Record<keyof T, true>; and if they are also named there, the runtime arm fails with `instanceId` is declared on Notification but the table has no such column
Two producers could not be reached from the console project and that is recorded in the file rather than worked around: lib/trigger-types.ts transitively drags Env and the Worker globals in, and the D1-shaped responses have no TypeScript declaration at all (SELECT *). Those are pinned by name against the migration column list — weaker, and labelled as such.
No known divergence remains in the 42 inspected. #616 should begin with an empty exemption list, which was the point.
Follow-ups filed rather than absorbed:#621 (structured handoff field), #622 (unclickable notification), #623 (the now-unfed props), #625 (RunDetail treats "expired" as finished — the #611 defect, in the console).
Verified:pnpm typecheck (10 projects), 531 test files / 9254 tests, biome, all 9 script gates, docs:drift, console vite build, and pnpm test:e2e — 413 passed across chromium + webkit, including the 320px/390px WebKit mobile arms.
Not verified: the seven remaining nominal-vs-structural judgements were made by reading the producer, not by observing a live response body for each. The four with a user-visible symptom (#1, #4, #5, #12) were traced to their render site.
CI green on 8aa63b7e, host deploy green, and the fix verified in the live console bundle: handoff_field is gone (0 occurrences) and addedAt is present (2). Closing.
Activity
fix(console): seventeen response types stop claiming fields the Worke…
serge-ivo commentedon Aug 15, 2026
Done in
8aa63b7ea9a2e9e6c56c4e0e2b2ba9c50b8ba1e5.AC1 — the enumeration, and it is seventeen, not fifteen
Denominator: 42 console-side API-response type declarations inspected, 17 diverged. The issue's caveat was right — only 4 of the 17 are nominal renames; 13 are structural, and one of those must not be renamed at all.
file:line)file:line)lib/types.ts:183KnowledgeDoc.createdAtworkers/api/src/agent-types.ts:80, writtenagent-do-knowledge.ts:80,:245addedAtlib/types.ts:182KnowledgeDoc.typeagent-types.ts:74-82source+sourceUrllib/types.ts:217Credential.historylib/credentials.ts:30, mapped:107recoveryHistory(colrecovery_history)lib/types.ts:56Instance.capabilities.boardColumnsroutes/instances.ts:315lib/types.ts:58Instance.capabilities.settingsSchemaroutes/instances.ts:315lib/types.ts:227Notification.instanceIdroutes/notifications.ts:14(SELECT *)agent_id, holding an agent idlib/types.ts:235Notification.createdAtcreated_atlib/types.ts:247Notification.datalib/types.ts:226Notification.read: booleanmigrations/0006:9INTEGER→0/1lib/types.ts:158RuntimeTask.needs_human: booleanpackages/browser-runner/src/types.ts:14TaskStatusvalue, not a fieldlib/types.ts:159RuntimeTask.handoff_reasonlib/types.ts:160RuntimeTask.handoff_fieldlib/types.ts:153RuntimeTask.resultbrowser-runner/src/types.ts:44output+errorlib/types.ts:166RuntimeEvent.timestamp(required)browser-runner/src/types.ts:57createdAtonlylib/types.ts:14Agent.config: objectroutes/agents.ts:137(SELECT *)/:idomits it entirelylib/types.ts:13Agent.creator_loginagents.ts:137,:267CASE … asalias in the browse querytabs/IndexingTab.tsx:43InstanceTrigger.action(4 members)lib/trigger-types.ts:34AC3 — what the user was not seeing, weighted
One had a visible symptom. #1: the doc viewer's "added on" stamp (
tabs/KnowledgeTab.tsx:506) has never rendered for anyone.tabs/IndexingTab.tsx:188had already hit this and worked around it locally withdoc as KnowledgeDoc & { addedAt?: string }— which is the evidence that the declaration, not the wire, was wrong.One was load-bearing and worse than a missing field. #12:
RunDetail.tsx:405readtask?.handoff_field || detail.replace(/^Needs your input…/i,"")… || "your answer".The left operand can never win, so the label on the box a user fills during a handoff has always been scraped out of prose, or degraded to the literal words "your answer". Filed as #621.
Two were dead optimisations. #4/#5:
routes/instances.ts:315strips both fields deliberately (measured: ~83 KB for 28 instances), andmy/instancesis the only endpoint any consumer ofInstancecalls.SettingsTab.tsx:58calls its prop "the fast path" — for an optimisation that has never once fired. The board first-paintsGENERIC_COLUMNS; settings first-paints empty. Both self-heal on the next fetch, which is why nobody reported them. Filed as #623.One corrects this issue's premise. #3 is described here as "the history is always empty". It is not — no component reads either name; the credential card renders
domainandusernameonly (KnowledgeTab.tsx:737-738). The cost so far is zero. It is fixed because the wrong name is what the next person to build that UI would reach for.One must NOT be renamed — AC2's escape hatch, used once. #6: matching the producer would give
navigate(/instances/<agent_id>), a broken route rather than a fixed one. The branch was deleted, and the gap it was hiding (a row with nourlis unclickable) is filed as #622.The rest cost nothing yet and were type-lies waiting to be believed. #14 is the sharpest:
timestampwas declared required directly above a comment admitting it "is often absent". It is always absent — nothing emits it anywhere — so the declaration promised every future reader a string that never arrives. #17 is notable for a different reason: the console's other copy of that union (TriggersSection.tsx:53) had all seven, so the console disagreed with itself as well as with the Worker. Both now point at one declaration.AC2 — every fix is on the consumer
No producer was renamed. The Worker's names were correct in all 17 cases; #4/#5 are a deliberate server decision with a measurement attached, and #6 is the case where the producer's name is right and still must not be adopted. So no API change is required or made.
AC4 — a test per fix, watched red
store/console/src/lib/types.test.ts. It is types, not assertions, because this defect class is invisible at runtime by construction — optional on both sides, so nothing throws and nothing logs — and a fixture-driven test is written from the same wrong belief as the code, so it agrees with the bug.Exclude<keyof Console, keyof Producer> extends nevercompares the declarations instead;store/console/tsconfig.jsonhasinclude: ["src"], so CI'stsc --noEmitcompiles it.Watched red, each one, before the fix went back:
createdAt/type→types.test.ts(48,7): Type 'true' is not assignable to type 'never', plus three real read siteshandoff_field/needs_human→ the same at theRuntimeTaskassertionrun_pipelinefrom the union → the guard plus three live comparison sites inTriggersSection.tsx, which is what proves the dedup actually connectedinstanceId/history→ compile error from the exhaustiveRecord<keyof T, true>; and if they are also named there, the runtime arm fails with`instanceId` is declared on Notification but the table has no such columnTwo producers could not be reached from the console project and that is recorded in the file rather than worked around:
lib/trigger-types.tstransitively dragsEnvand the Worker globals in, and the D1-shaped responses have no TypeScript declaration at all (SELECT *). Those are pinned by name against the migration column list — weaker, and labelled as such.AC5 — #616 now starts from zero
No known divergence remains in the 42 inspected. #616 should begin with an empty exemption list, which was the point.
Follow-ups filed rather than absorbed: #621 (structured handoff field), #622 (unclickable notification), #623 (the now-unfed props), #625 (
RunDetailtreats"expired"as finished — the #611 defect, in the console).Verified:
pnpm typecheck(10 projects), 531 test files / 9254 tests, biome, all 9 script gates,docs:drift, consolevite build, andpnpm test:e2e— 413 passed across chromium + webkit, including the 320px/390px WebKit mobile arms.Not verified: the seven remaining nominal-vs-structural judgements were made by reading the producer, not by observing a live response body for each. The four with a user-visible symptom (#1, #4, #5, #12) were traced to their render site.
serge-ivo commentedon Aug 15, 2026
CI green on
8aa63b7e, host deploy green, and the fix verified in the live console bundle:handoff_fieldis gone (0 occurrences) andaddedAtis present (2). Closing.feat(guard): check-console-types.mjs ratchets 108 anonymous api<{…}> …
fix(notifications): unclickable no-url rows get an agent_id fallback (#…
feat(console): a home-screen shortcut to one instance, and notificati…