diff --git a/AGENTS.md b/AGENTS.md index 2b3e2f2..05507c7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,7 +28,7 @@ Paste the three green tails into the PR body. ## Booting with the demo seed — read before you count rows -The demo seed (`src/data/`, 801 rows incl. the 7 logins in `README.md`) is scoped `env: ['dev', 'test']` +The demo seed (`src/data/`, 818 rows incl. the 7 logins in `README.md`) is scoped `env: ['dev', 'test']` and loads **only when the boot's `NODE_ENV` resolves to development or test**. The CLI pins it for you: ```bash @@ -43,7 +43,7 @@ have checked the boot log for these lines**: | State | What the log says | Rows | |:--|:--|:--| -| seeded | `INFO [ats] demo seed enabled: NODE_ENV=development …` then `[Seeder] Seed loading complete {"inserted":801,…,"errored":0}` | 80 `ats_candidate`, 7 sign-ins work | +| seeded | `INFO [ats] demo seed enabled: NODE_ENV=development …` then `[Seeder] Seed loading complete {"inserted":818,…,"errored":0}` | 80 `ats_candidate`, 7 sign-ins work | | skipped | `WARN [ats] demo seed skipped: NODE_ENV=production …` (replayed under the banner at the default log level) and, at `--log-level info`, `[SeedLoader] Environment 'prod': skipped 16 dataset(s) …` | 0 everywhere, every persona answers `Invalid email or password` | If you exported `NODE_ENV=production` in your shell, `objectstack dev` keeps it and you get the skipped diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 00a5fbc..b935d7f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,7 +62,7 @@ gap; finding one is the finding. **Measure, do not assert.** This repository's review culture is that a claim in a PR body comes with the command that produced it and the number it produced. "It works" is not a sentence in a PR here; "the -Quillstone admin reads 5 jobs / 27 applications / 2 offers and 0 of Harborline's" is. Several cards have +Quillstone admin reads 5 jobs / 27 applications / 3 offers and 0 of Harborline's" is. Several cards have been overturned by their own developer measuring the premise — that is the standard, not the exception. ## Rules that are easy to break diff --git a/DESIGN.md b/DESIGN.md index 283af56..0edd682 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -315,7 +315,7 @@ record.id in current_user.applicant_candidate_ids 本机构申请人 | ats_candidate | 80 | 带头像;经验与技能有梯度 | | ats_application | 200 | 按漏斗铺:applied 88 · screening 46 · interview 28 · offer 14 · hired 9 · rejected 15 | | ats_interview | 40 | **未来两周内**有排期 | -| ats_offer | 14 | 3 条 `pending_approval` | +| ats_offer | 23 | 14 条挂在 offer 阶段的投递上(3 条 `pending_approval`)· 9 条 `accepted`,每个 hired 投递恰一条(#53) | | ats_inquiry | 8 | 全部 `new`;5 位无候选人行的投递者、3 位已入库候选人(转换后挂到既有行);2 条 Quillstone、2 条 Harborline | | ats_skill / ats_credential_type | 60 / 15 | 字典先行 | diff --git a/README.md b/README.md index dc12b45..27365f6 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ pnpm dev # REST + Console on http://localhost:3000/_console/ — sign i ``` There is **no hosted demo yet** — nothing is deployed, so there is no link to give you; the three commands -above are the way to try it, and they boot the 809-row demo seed (the fresh-clone timing is measured in +above are the way to try it, and they boot the 818-row demo seed (the fresh-clone timing is measured in the card-14 pull request, not asserted here). What you get is one app with three audiences, each seeing exactly the navigation group its position unlocks: @@ -85,9 +85,9 @@ or `NODE_ENV=production` none of these rows is created and every sign-in below a | Boot | `NODE_ENV` the CLI pins | Demo rows | These logins | |---|---|---|---| -| `pnpm dev` · `objectstack dev` | `development` (when unset) | 809 seeded | work | +| `pnpm dev` · `objectstack dev` | `development` (when unset) | 818 seeded | work | | `objectstack start` · `objectstack serve` | `production` (when unset) | none | do not exist | -| `NODE_ENV=development objectstack start` | as exported | 809 seeded | work — deliberate opt-in | +| `NODE_ENV=development objectstack start` | as exported | 818 seeded | work — deliberate opt-in | | Sign in as | Password | Who | Sees | |---|---|---|---| @@ -99,8 +99,8 @@ or `NODE_ENV=production` none of these rows is created and every sign-in below a | `admin@harborline.example` | `demo1234` | Employer administrator, Harborline | **Hiring** group | | `candidate01@mail.example` | `demo1234` | Job seeker | **Job Seeker** group | -Employer isolation works: signed in as Quillstone you see 5 jobs, 27 applications, 2 offers, -3 team members and 2 inquiries; as Harborline, 5 / 31 / 2 / 3 / 2 — and neither sees a single row of +Employer isolation works: signed in as Quillstone you see 5 jobs, 27 applications, 3 offers, +3 team members and 2 inquiries; as Harborline, 5 / 31 / 4 / 3 / 2 — and neither sees a single row of the other's. ### The public application form diff --git a/docs/screenshots/README.md b/docs/screenshots/README.md index f009017..3070234 100644 --- a/docs/screenshots/README.md +++ b/docs/screenshots/README.md @@ -8,6 +8,9 @@ OS_PLATFORM_OWNER_EMAIL=admin@objectos.ai npx objectstack dev --fresh --database **every shot on the memory driver**, after the boot log's `[Seeder] Seed loading complete {"inserted":809,…,"errored":0}`, in a headless Chromium at 1440 × 900. +The seed has since grown to 818 rows — #53 added one `accepted` `ats_offer` per hired application, nothing +else — so a boot today logs `{"inserted":818,…}`; no screen below reads `ats_offer`, and the stage counts the +shots show (88 / 46 / 28 / 14 / 9 marketplace-wide, 10 / 8 / 5 / 2 / 1 for Quillstone) did not move. The driver matters: on the default (sqlite) driver the four tenancy-scoped objects return no rows to the platform personas ([#39](https://github.com/objectstack-ai/ats/issues/39)), so the Platform group's review queues photograph empty there. The demo logins are in the [README](../../README.md#demo-logins); the diff --git a/src/dashboards/employer-hiring.dashboard.ts b/src/dashboards/employer-hiring.dashboard.ts index ef31d3e..40ebf79 100644 --- a/src/dashboards/employer-hiring.dashboard.ts +++ b/src/dashboards/employer-hiring.dashboard.ts @@ -18,8 +18,10 @@ import type { Dashboard } from '@objectstack/spec/ui'; * - the duration lives across two objects and needs a stored column * (`ats_application.days_to_offer`, stamped when the offer is written), * which is an object + hook change outside this card; - * - the demo seed has no offer on any hired application (its 14 offers all - * sit on `offer`-stage applications), so the tile would read empty anyway. + * - the demo seed does now carry one `accepted` offer per hired application + * (#53 — before it, all 14 offers sat on `offer`-stage applications and the + * tile would have read empty), so what keeps the tile out is the two + * reasons above, not the data. * The pipeline-by-stage bar takes its place so the surface shows the same * scoping at a glance; the tile returns with a card that adds the column. * diff --git a/src/data/demo-en/offer.seed.ts b/src/data/demo-en/offer.seed.ts index cb6ef70..b416c8f 100644 --- a/src/data/demo-en/offer.seed.ts +++ b/src/data/demo-en/offer.seed.ts @@ -3,7 +3,7 @@ import { Offer } from '../../objects/offer.object.js'; import { buildOffers } from '../shared/build.js'; import { pack } from './pack.js'; -/** demo-en · 14 offers, 3 of them pending_approval. */ +/** demo-en · 23 offers: 14 on offer-stage applications (3 pending_approval) and one `accepted` per hired application (9). */ export const OfferSeed = defineSeed(Offer, { externalId: 'display_name', mode: 'upsert', diff --git a/src/data/demo-zh/offer.seed.ts b/src/data/demo-zh/offer.seed.ts index fa806b4..194a6e5 100644 --- a/src/data/demo-zh/offer.seed.ts +++ b/src/data/demo-zh/offer.seed.ts @@ -3,7 +3,7 @@ import { Offer } from '../../objects/offer.object.js'; import { buildOffers } from '../shared/build.js'; import { pack } from './pack.js'; -/** demo-zh · 14 offers, 3 of them pending_approval. */ +/** demo-zh · 23 offers: 14 on offer-stage applications (3 pending_approval) and one `accepted` per hired application (9). */ export const OfferSeed = defineSeed(Offer, { externalId: 'display_name', mode: 'upsert', diff --git a/src/data/index.ts b/src/data/index.ts index 357518d..29b4256 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -26,7 +26,7 @@ import { createDemoSeedGatePlugin, scopeToDemo } from './demo-seed-gate.js'; * ## Demo only — never production (#42) * * Whichever set is selected is scoped `env: ['dev', 'test']` by - * `scopeToDemo`, so the seed loader drops all of it — the 794 ATS/identity + * `scopeToDemo`, so the seed loader drops all of it — the 811 ATS/identity * rows AND the 7 `sys_account` logins whose passwords are in the README — on * any boot whose `NODE_ENV` resolves to production. `objectstack dev` sets * `NODE_ENV=development` and seeds; `objectstack start`/`serve` set diff --git a/src/data/shared/build.ts b/src/data/shared/build.ts index c548610..967e7ff 100644 --- a/src/data/shared/build.ts +++ b/src/data/shared/build.ts @@ -108,6 +108,8 @@ export { const daysAgo = (n: number) => cel`daysAgo(${n})`; const daysFromNow = (n: number) => cel`daysFromNow(${n})`; +/** A signed calendar offset: `n` days ahead, or `-n` days ago — for rows whose dates straddle seed time (accepted offers). */ +const dayOffset = (n: number) => (n < 0 ? daysAgo(-n) : daysFromNow(n)); /** A calendar day from seed time at a wall-clock hour: the calendar helpers land on UTC midnight, so add a duration. */ const dayAt = (day: number, hour: number, minute: number) => cel`daysFromNow(${day}) + duration(${minute > 0 ? `${hour}h${minute}m` : `${hour}h`})`; @@ -487,7 +489,9 @@ export function buildOffers(pack: LocalePack): TenantScopedSeedRecordOf