Commit Graph

16 Commits

Author SHA1 Message Date
3e654c2bf7 chore(ts-migration): migrate seed fixtures to TypeScript
Phase 4c, completing `src/api`. Both files emit byte-identical JavaScript -
86,831 and 6,449 bytes - and neither needed a single annotation: they are data
and pure helpers, and inference already describes them.

Neither reaches the production bundle. No module under `src` imports either one;
`base44Client` mentions `attendanceSeed` in a comment and nothing more. They
exist for `skill-check.mjs`, `owliver-capture.mjs` and `seed-fixture.mjs`, which
is why this was the safest phase in the whole migration and why it was left
until the transport layer was done.

`npm run seed:check` still answers "seed.json is stale", which it has since
before this migration began - the backend fixture drifted from `src/api/seed`
independently of any of this. What matters here is that it ANSWERS: the
generator loaded the renamed module through the Phase 0 resolver rather than
failing to find it.

tsc unchanged at 40, npm test 1684/1691 with the same seven failures, lint 0
errors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8
2026-09-17 23:00:29 +05:30
1133eca16a chore(ts-migration): migrate base44 client to TypeScript
Phase 4b-ii, and the last file in the transport layer. Type-only: the plain
emitted JavaScript is byte-identical at 9,302 bytes.

This file owns the session, so the annotations stay at the edges and nothing
about the auth model moves. `SESSION_KEY`, the localStorage mirror, the three
module-level mutables, the shared hydration promise, the `{ ...user }` spreads,
`window.location.href` on logout and `window.location.reload()` in
`resetDemoData` are all exactly as they were.

Nine sites, eight of them parameter types. The ninth is `hydration`, which needs
`Promise<any> | null` because the runtime already assigns both: the shared first
`GET /me`, then `null` once it has been consumed or superseded by a login.
Inference would have fixed it at `Promise<any>` and rejected the assignments
that make the sharing work.

`entities` is deliberately NOT annotated, and neither is `createEntity`'s
return. A probe run during the inspection showed what annotating it costs:
`Record<EntityName, EntityClient<any>>` makes `list()` return `Promise<any[]>`
where inference gives `Promise<any>`, and `agentStore.js` does
`qc.getQueryData(KEY) || (await ...list(...))`. React Query types
`getQueryData` as `unknown`; `unknown || any` collapses to `any` while
`unknown || any[]` stays `unknown`, and `.find` on the next line stops
compiling. Two new errors in a file this phase does not migrate. The generated
record types and `EntityClientFor<K>` are ready for the phase that migrates the
forty-five call sites in `krowHooks.js`.

The auth returns are likewise left inferred rather than typed `User`. That was
checked, not assumed: `admin/Profile.jsx` reads `user.avatar_url`, which is
neither a column on `users` nor part of the `/me` projection in `me.go` - it is
always undefined at run time. Typing the return would have turned a latent dead
branch into two compile errors in a file this phase does not touch. Worth
knowing about separately; not this commit's business.

One stale comment is kept verbatim - `@param {any} request` above
`owliver.suggestions`, which names a parameter that does not exist. Removing it
was the only remaining difference in the emitted output, and byte-identity is
worth more here than tidying a comment. Flagged for a later docs pass.

Verified: tsc 42 -> 40 with a set-difference showing two removed and none added;
npm test 1684/1691, the same seven failures as the parent commit (six documented
as deliberate in scripts/__baseline__/README.md, one the stale backend fixture);
lint 0 errors; build succeeds with the API origin inlined. This is also the
first run of the suite with a source-text-inspected file migrated - the harness
fix in 02a2ab0 is what makes it possible.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8
2026-09-17 22:59:15 +05:30
64140c7add chore(ts-migration): migrate ai engine to TypeScript
Phase 4b-i. Type-only, and the production bundle proves it: built from this
commit's parent and from this commit, all six chunk hashes match. The plain
emitted JavaScript is byte-identical at 34,095 bytes.

Four annotation sites, no logic touched:

  - `ROLE_TITLES` and `AVAILABILITY_TOKENS` get `[RegExp, string][]`. Left to
    inference the element widens to `string | RegExp`, which has no `.test`.
    Explicit tuples rather than `as const`, which would also have worked and
    would additionally have made the arrays readonly - a change to the type this
    module publishes for no benefit it uses.

  - `ROUTES` gets `[RegExp, (prompt: string) => any, number][]`, because all
    three positions are used for what they are: `.test()` on the first, a call
    on the second, `think(ms)` on the third.

  - `invokeLLM` gets a real parameter type. It inferred `{ prompt?: string }`
    from its own destructuring defaults, and that single inference was
    responsible for nine errors in files this commit does not touch - eight in
    `krowAi.js`, one in `provingGround.ts` - every one of them a caller passing
    `response_json_schema` or `model`, which the real integration accepts.
    Naming the options type fixes all nine from here.

  - `uploadFile` gets `{ file?: File }`.

The return type of `invokeLLM` stays `Promise<any>`, deliberately. The ten
handlers behind the router return ten different shapes, and `krowAi.js` branches
on the result at run time - `typeof res === 'string' ? res : res.text || String(res)`
- which a precise union would reject on every branch without a `.text`. The
looseness is the contract, not an omission.

`InvokeLLMOptions` is exported as a type only; the runtime exports are still
exactly `invokeLLM` and `uploadFile`.

Verified in isolation from the parallel feature work (f96f128 plus this file):
tsc 53 -> 37, a set-difference against the baseline showing sixteen removed and
none added; skill-check 1641/1642 with only the known stale-fixture failure;
Owliver baseline 59/59; lint 0 errors; build succeeds with the API origin
inlined; the nine Owliver baseline artifacts unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8
2026-09-17 17:23:28 +05:30
f96f128839 chore(ts-migration): migrate API transport to TypeScript
Phase 4a: `demoUser` and `httpClient`. Type-only. The production bundle is
byte-identical - built from this commit's parent and from this commit, all six
chunk hashes match.

`httpClient` is the contract boundary, so the annotations are deliberately
conservative:

  - `KrowApiError` is a CAST, not a class. The three error sites still build a
    plain `Error` and assign `.name`, `.status`, `.code` and `.details` onto it
    exactly as before. `class KrowApiError extends Error` would have read
    better and changed three things that callers depend on: the prototype
    chain, `instanceof`, and how `name` comes to be set.

  - `RESOURCE_PATHS` becomes `Record<EntityName, EntityResourcePath>`. That is
    the first thing in the repo to check the eighteen entity names against the
    eighteen in `ENTITY_NAMES`; until now the two lists agreed only by habit,
    and a divergence would have surfaced as `base44.entities.Whatever` being
    undefined with nothing to say why.

  - `createEntity`'s parameter stays `string` and its return stays inferred.
    Annotating the return `EntityClient<any>` was tried and reverted: it makes
    `list()` return `Promise<any[]>` where inference gives `Promise<any>`, and
    `agentStore.js` does `qc.getQueryData(KEY) || (await ...list(...))`. React
    Query types `getQueryData` as `unknown`; `unknown || any` collapses to
    `any`, `unknown || any[]` stays `unknown`, and `.find` on the next line
    stopped compiling. Two new errors in a file this phase does not migrate,
    for no gain. `EntityClientFor<K>` is ready for the phase that migrates
    those consumers.

Unchanged and verified in the emitted output: `credentials: 'include'`, both
header branches on `body === undefined`, the URLSearchParams query encoding with
its repeated-array and undefined-omission rules, the `payload.data` unwrap that
drops `meta`, the verbatim server message, `status: 0` / `code: 'unreachable'`
for a transport failure, the `-created_date` and `limit` defaults, path
construction through `encodeURIComponent`, and `bulkCreate`'s sequential
`this.create` loop.

`DEMO_USER` is annotated `User`, which does real work: unannotated,
`role: 'admin'` widens to `string` and the default shape did not satisfy the
type the app uses for the thing it defaults.

Verified in isolation from the parallel feature work (d1425f9 plus these two
files): tsc 64 -> 53, the eleven removed being exactly this file's, and a
set-difference against the baseline showing none added. skill-check 1641/1642
with only the known stale-fixture failure, Owliver baseline 59/59, lint 0
errors, build succeeds with the API origin inlined.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8
2026-09-17 12:16:16 +05:30
e02a0c23d4 Make a conversation a registry, and add the second one
Some checks failed
CI / check (push) Failing after 4m57s
`positionFlow.js` was five per-domain concerns in one file — a field table, an
`@`-token resolver, a sentence extractor, a commit vocabulary and a set of
outcome renderers — and only the control flow between them was general.
Everything else knew it was creating a job posting. Adding a second
conversation meant a second copy of all of it.

So the control flow is now `conversationFlow.js` and each kind of record is a
REGISTRY. Which conversation a skill runs is the skill's own `flow:` line,
resolved through a map: `routing.js` used to say `if (skill.id ===
'create-position')`, which made a second conversational skill a change to the
router rather than a file on disk — the `if agent_key == ...` shape the
platform rules out one level up. The panel's write callback is likewise a map
keyed by flow id instead of an `onCreatePosition` prop, and the outcome wording
comes off the registry, so nothing in the panel names a kind of record any
more.

`create-employee-role` is the second registry. The worker is asked for and
never assumed: a conversation that names nobody re-asks rather than falling
back to the session, because an operator records this on somebody's behalf. Its
`extract` is deliberately narrower than the posting's — "bartender, weekends,
$30/hr" settles three fields and leaves the subject alone, since guessing WHO a
record is about from a fragment is how a role gets filed against the wrong
person.

THE CONFIRMATION STEP ACCEPTED "create position" AND SILENTLY REJECTED "create
positions" — the plural the Positions page itself uses. An anchored regex missed
it, and the reader got the summary back with no indication of what was wrong
with what they said, which is indistinguishable from the screen not having
updated. Matching is now exact membership against a normalized reply, so a
vocabulary is a list of phrases somebody can read rather than an expression
somebody has to parse.

Two bugs in `extractRole`, both of which fabricated a value nobody typed on the
one field a position cannot be created without:

  - The phrase pattern marks "new" as the role by the same grammar that marks
    "sous chef", so "create new position" opened the conversation titled "New".
    The scaffolding is a PHRASE at least as often as a single word, so a
    per-word test still produced "Brand New" and "One More". Scaffolding words
    are now stripped to DECIDE whether the phrase named anything, and the
    ORIGINAL phrase is returned when it did — strip to test, never to rewrite,
    or "second chef" becomes "Chef" and the cure is worse than the bug.

  - `(?:a|an)?\s*` has no word boundary, so it matched the leading "a" of
    "another" and the capture began mid-word. That mangled scaffolding into
    "Nother New" and, worse, corrupted every role introduced with "an":
    "create an open kitchen lead position" titled the position "N Open Kitchen
    Lead". A real role, typed correctly, silently wrong. Found by mutation
    testing the first fix.

`@companies` and `@workers` resolve from data the panel already holds — postings
and profiles the API has already scoped to the caller — so neither widens
anybody's view and neither costs a request. The company list is deliberately
unsorted: `useJobPostings` asks for `-created_date`, so the clients staffed for
most recently come first, and the panel does no ranking of its own. That last
part is a rule the suite enforces structurally, and it is the right rule — a
second opinion formed in the panel outranking the server's is exactly the kind
of thing that decays quietly.

`npm test` now refuses a conversation step whose field its registry does not
define. `stepsOf` drops unknown fields, so a typo means the flow asks fewer
questions than the file lists — and a skill whose steps are ALL unknown asks
none, jumps to the summary, and offers to write an empty record. Nothing errors
and the Markdown still reads correctly. 970 checks, up from 924; the new ones
walk both conversations end to end, because a wrong answer at the confirmation
step re-renders the same summary a right answer does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJvibeSc1JYXjatankqM1g
2026-09-02 15:29:48 +05:30
88c412fda6 skill issue 2026-08-28 12:22:08 +05:30
eaf08e061d agnets done 2026-08-28 11:02:02 +05:30
ab9adde6cd update the chatbox 2026-08-25 18:56:07 +05:30
02eb48af99 fix mobile screen issues 2026-08-24 13:16:15 +05:30
b2e6868824 update agents skill design 2026-08-20 18:18:10 +05:30
d3f7f439f6 update Markdown skills 2026-08-19 17:36:27 +05:30
7fa21a4513 fix error line 2026-08-17 17:33:56 +05:30
cccada9bd2 position ui and owliver update 2026-08-14 01:18:59 +05:30
1c653264b5 update position page design 2026-08-13 21:41:14 +05:30
3beb66059b update ui design for all pages 2026-08-12 18:56:56 +05:30
90f14a73b2 Initial commit 2026-08-07 23:53:15 +05:30