join city
Веб-сервис подбора до 3 подрядчиков для мероприятия по городу, дате, формату, категории и бюджету, с объяснением каждого выбора и метками…
О проекте
Команда: «The Power of Dreams»
Кейс HackAlem.ai: Креативные индустрии — Firebird
Стадия: Доведённый продукт
Задача кейса: «#79-lite»: подобрать до 3 подрядчиков для мероприятия и объяснить выбор
Суть
Веб-сервис подбора до 3 подрядчиков для мероприятия по городу, дате, формату, категории и бюджету, с объяснением каждого выбора и метками происхождения данных.
Как устроено
Каталог подрядчиков из raw/dataset.csv загружается в Node.js-backend; детерминированный доменный слой отбирает кандидатов по занятости в календаре, цене, специализации и пожеланиям. OpenAI опционально интерпретирует свободные пожелания в brief и дополняет объяснения фактами из описаний подрядчиков, а проверка по исходным данным отбрасывает выдуманные характеристики; без ключа работает catalog_fallback. Next.js-интерфейс и API в одном контейнере Docker Compose с healthcheck.
Ключевые решения
- rule-based фильтрация и ранжирование
- LLM-интерпретация пожеланий
- evidence validation
- catalog fallback
- OpenSpec spec-driven разработка
Особенности
Строгий spec-driven процесс (OpenSpec, .proposals, версионированные контракты v1/v2, 104 коммита за 4 часа); объяснения размечены по источнику (каталог/AI/смешанный), smoke-check из 7 кейсов внутри контейнера.
Стек
TypeScript · Next.js · React · Node.js · Tailwind CSS · csv-parse · Docker Compose
Модели
OpenAI API
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-83108e9d-the-power-of-dreams.
README
README.mdjoin city — подбор подрядчиков для мероприятий
Описание проекта
join city помогает организаторам мероприятий в Казахстане находить подходящих подрядчиков без долгого изучения каталога. Пользователь указывает город, дату, формат мероприятия и бюджет, а сервис предлагает до трёх подходящих вариантов с понятным объяснением каждого результата.
Система учитывает занятость по календарю, стоимость, специализацию и дополнительные пожелания. Искусственный интеллект помогает дополнять объяснения фактами из описаний подрядчиков, а проверка по исходным данным защищает от выдуманных характеристик. Это помогает сравнить варианты и принять обоснованное решение.
Технологические теги
Next.js, React, TypeScript, Node.js, Tailwind CSS, OpenAI API, Docker
Тематические теги
Искусственный интеллект, Подбор подрядчиков, Организация мероприятий, Объяснимые рекомендации
Application overview
A local web application for hackathon task #79-lite.
The Russian-language form asks for city, event date, event format, contractor category and budget in KZT, with optional language and duration under Дополнительные условия. Results show each contractor's name, category, city, starting price, explanation and data-provenance labels. The catalogue contains anonymized and synthetic profiles; this is a demonstration of selection, not a booking service or a source of confirmed contractor availability.
Quick start with Docker (recommended for judges)
Install and start Docker Desktop with
Linux containers on Windows/macOS, or Docker Engine with the
Compose plugin on Linux.
Use Compose 2.20 or later (Compose 5 is also supported) and a modern browser.
The first build needs internet access to download the base image, OS build tools
and npm packages; allow several minutes. Node.js and npm run inside Docker.
No database, GPU, provider account or .env is required for catalogue-only use.
Clone the repository, or download and extract its ZIP. From its root:
git clone https://github.com/BAITC-Hacks/hack-83108e9d-the-power-of-dreams.git
cd hack-83108e9d-the-power-of-dreams
docker compose up --build --wait --wait-timeout 120
If you already have the project, run only the last command in its root. Open the application and follow the primary scenario below. The command leaves the application running in the background and waits for a successful catalogue API healthcheck. The 120-second readiness limit starts after the build; it does not limit initial downloads. The image includes the supplied CSV and all runtime files, uses a non-root user, and exposes the application only on this computer.
Check status and run the supplied HTTP smoke check without installing host Node.js:
docker compose ps
docker compose exec -T app node scripts/docker/smoke.mjs
Expected: service app is healthy; the smoke check reports seven passing
selection/validation cases and Container HTTP smoke passed. On a fresh checkout,
the modes are catalog_fallback, not_needed. This uses the real catalogue and
HTTP API; it does not evaluate live AI explanation quality. If you configure
OpenAI, the smoke check can make four billable provider requests.
What Docker installs and starts
| Component | How it is supplied |
|---|---|
| Node.js 24.4.1 / npm 11.4.2 | Official Linux base image pinned by digest in Dockerfile |
| Application dependencies | npm ci installs the committed lockfile in a clean build stage; npm prune --omit=dev removes development packages from the runtime stage |
| Build and verification | The image build runs npm run typecheck, npm test and npm run build; a failing check stops the build |
| Web interface and API | One Compose service, app, serves both through the same port |
| Catalogue and wishes evidence | CSV is copied into the image; the validated wishes index is bundled during the application build |
| OpenAI | Optional external API, enabled only by runtime credentials; it is not a Docker service |
There is no database, Redis, queue, separate frontend container or dependency
service to start. No source bind mount or persistent volume is needed. The
runtime image includes production packages and compiled assets; browser-test
tools are build/development dependencies. Do not run npm ci inside the running
container: rebuild the image when dependencies change.
This Compose configuration is for local evaluation: the host port binds to
127.0.0.1 and is not accessible from another computer. Public hosting requires
separate network/TLS/access configuration. User results live in browser memory;
reload clears them. Rebuilding is required after catalogue or source changes.
To inspect startup problems or stop and remove this project's containers/network:
docker compose logs --tail 100 app
docker compose down
If port 3101 is occupied, set APP_PORT=3111 in a root .env (create it if absent,
or edit that one setting without replacing existing values), run the launch
command again and open the alternate address.
Restart after configuration changes with
docker compose up --wait --wait-timeout 120; rebuild with --build after source
changes. docker compose down preserves repository files and your local .env.
Optional AI explanations in Docker
Create a root .env by copying .env.example with your file manager, then fill
only OPENAI_API_KEY; OPENAI_MODEL is optional. Preserve an existing .env.
Run docker compose up --wait --wait-timeout 120 to recreate the service with the
new settings. No image rebuild is needed. A funded OpenAI project, model access
and outbound internet are required; each non-empty selection can make one
billable request for a submission without wishes. Parsing wishes makes one
separate billable request; confirmed wishes use local matching. Missing/failed/rejected AI evidence retains the existing
labelled catalogue or mixed explanations.
Compose reads .env for substitution and forwards only OPENAI_API_KEY and
OPENAI_MODEL to the app. Existing shell variables take precedence, including
an explicitly empty value. A configured key enables live calls automatically.
To use catalogue-only mode, remove/blank the key in .env and unset any shell
override before recreating the service. In Compose .env, use single quotes
around literal values containing $ or #; unlike the direct Node.js loader,
Compose expands variable references in unquoted/double-quoted values.
Credentials are supplied at runtime; .env files are excluded from the build
context and image. Keep them private and out of Git. Avoid sharing expanded
docker compose config or container environment output, which can contain keys.
| Docker symptom | Action |
|---|---|
| Cannot connect to the Docker daemon / missing Docker Desktop pipe | Start Docker Desktop, enable Linux containers and wait for its engine; docker info must succeed. |
Unknown --wait option | Update Compose to 2.20 or later. |
| Build cannot download images or packages | Check internet/proxy access to Docker Hub, Debian and npm registries, then repeat the build. |
| Port is already allocated | Set a free APP_PORT as described above. |
| Service is unhealthy or readiness times out | Read docker compose logs --tail 100 app; after repairing the cause, repeat docker compose up --build --wait --wait-timeout 120. |
Container verification and its exact revisions/limitations are recorded in the Docker delivery task card. The 23 September 2026 recheck covers clean dependency installation, the current production image, HTTP/browser scenarios, confirmed-wishes evidence and an alternate-port restart. With explicit authorization, two live OpenAI requests also passed: source-based explanations and wishes interpretation, followed by local selection using the interpreted conditions. Exact evidence and limits are recorded in the task card.
Alternative quick start with Node.js
1. Install the prerequisites
- Install Node.js 24.4.1 or a newer 24.x release from the official Node.js download page. On Windows, use the installer for your architecture with npm included, then reopen your terminal. The recorded project environment is Node.js 24.4.1 with npm 11.4.2; these are the baseline versions in package.json.
- Install Git if you will clone the repository. You can also download and extract the repository ZIP and open its root folder.
- Use a modern browser. Internet access is needed to download dependencies and, when enabled, to call OpenAI.
Check that the tools are available:
node --version
npm --version
No database, Docker, Python, WSL, GPU, NVIDIA account or separate backend service is required for this Node.js route. OpenAI access is optional for catalogue-only operation. Historical GPU experiments are not part of application deployment.
2. Get the project and install dependencies
If you already have the repository, open a terminal in its root (the directory
containing package.json) and skip the first two commands:
git clone https://github.com/BAITC-Hacks/hack-83108e9d-the-power-of-dreams.git
cd hack-83108e9d-the-power-of-dreams
npm ci
npm ci installs the runtime and development dependencies pinned in
package-lock.json. Do not install Next.js, React or
csv-parse individually or globally. Keep development dependencies installed
for the build and checks. No separate dataset download is needed:
raw/dataset.csv is included in the repository.
3. Choose the explanation mode
Without OpenAI: skip configuration on a fresh checkout. The app still reads the supplied CSV, selects contractors and explains matching format and budget. It labels these explanations as generated from catalogue fields without AI.
With OpenAI: before starting the server, create the local configuration:
node scripts/secrets/setup.mjs
Open the root .env locally and fill OPENAI_API_KEY. OPENAI_MODEL is optional;
absent or blank uses gpt-5.6-luna. The setup command preserves an
existing .env. Verify that the key is present without displaying it:
node scripts/secrets/check.mjs OPENAI_API_KEY
This checks configuration presence only. Live use requires a funded OpenAI
project, access to the configured model and outbound network access. Each
submission without wishes and with selected contractors can make one billable request.
Parsing wishes makes one separate request; selecting with confirmed wishes is local. Existing
environment variables take precedence over .env; an already configured key
enables live calls. See configuration details for
precedence, safe handling and optional settings.
4. Build and start
Run from the repository root:
npm run build
npm start -- --port 3000
Keep the terminal running and open the application.
Stop the server with Ctrl+C. Both start scripts bind to 127.0.0.1 for local
access. Retain raw/ and back/ alongside the application: the server loads
the CSV and the existing OpenAI transport from these directories at runtime.
Copying only the build output is insufficient.
Development mode
After installation and any optional configuration, use this instead of the build/start pair while editing the app:
npm run dev
Open the development server. To choose a free port,
use npm run dev -- --port 3102; for a production build use
npm start -- --port 3102. These commands follow the
Next.js CLI options.
Try the primary scenario
Enter the following values and press Подобрать:
| Field | Value |
|---|---|
| City | Алматы |
| Date | 2026-10-10 |
| Event format | корпоратив |
| Category | Ведущий |
| Budget | 1500000 KZT |
Expected with the supplied catalogue: 10 candidates, 5 eligible, with these three cards in order: Куррапика (HK-88430), Аня Форджер (HK-29829), Сон Гоку (HK-27222). Selection and order are the same with or without OpenAI; the explanations depend on whether validated source quotes are available.
Change the budget to 1 and submit again: the app should display a normal empty result. Changing fields alone does not make an AI request. To check a different date, change the date and press Подобрать again.
For a short demonstration, continue with these inputs (leave optional fields blank):
| Case | Change from the primary scenario | Expected result |
|---|---|---|
| Rare category | Category Флорист, format свадьба, budget 500000 | One card: HK-39372; explanation of the incomplete three-card set |
| No category in city | Rare inputs, city Зарубежье | Explicit category-absent outcome |
| Busy-date change | Primary inputs, date 2026-10-11 | HK-44923, HK-27222, HK-44733; date comparison explains busy marks |
| Price-order date change | Primary inputs, submit October 1 then October 6 | HK-75012 is replaced by HK-29829 through starting-price order, not a new busy mark |
Reset restores the primary defaults. Without wishes, filtering and price/ID order select the cards; optional AI supplies source quotes. Starting prices and calendar marks never guarantee booking. Dataset and software provenance is in THIRD_PARTY.md.
Try AI wishes
With a funded OpenAI key configured, restore the primary inputs and enter Нужен ненавязчивый ведущий, без принудительных конкурсов in Пожелания к подрядчику. Press Разобрать пожелания, review the displayed interpretation, remove any incorrect condition or edit the text and parse again, then press Подобрать. Interpretation alone does not submit a recommendation.
Expected: Хаул (HK-77838) moves into first place, followed by HK-88430 and HK-29829. His profile explicitly states a discreet style. Compulsory contests remain Нужно уточнить: avoiding compulsory contests does not mean rejecting all contests. Cards show literal profile evidence, contradictions when present, unknown wishes, and a question to ask the contractor. These are catalogue claims, not independently verified promises. Budget 900000 still excludes Хаул.
Every hard-eligible profile is considered. Confirmed wishes order by fewer conflicts, more supported matches, starting price, then ID; they never override city/category, date, format, budget, language or duration. Up to three cards are shown. Identical confirmed conditions, catalogue and policy give identical order after restart. Parsing the same free text again can produce a different interpretation, so review it before selection. Date comparisons reflect this wishes-based order instead of attributing every replacement to price.
The current vocabulary covers 18 styles/features, with 48 verified literal assertions across the 66-profile catalogue. Other wishes and absent claims stay unknown; absence is not evidence of unsuitability. Text is limited to 1000 Unicode characters and six interpreted conditions. Changing text invalidates its interpretation. Reset cancels pending work and clears wishes. If interpretation is unavailable, the text and previous result remain; retry explicitly or clear the optional field to use ordinary selection. No key is required for ordinary selection. An outdated evidence index fails wishes-based selection explicitly rather than silently replacing it with price order.
The implementation and measured model comparison record Luna/Terra quality, prompt repairs, latency and conservative API cost. The bounded experiment selected Luna; this is not a guarantee of perfect interpretation for arbitrary customer text.
For developers, node scripts/brief/compare.mjs --live runs the 30 synthetic
cases twice on each of Luna and Terra. It requires a funded key and incurs
charges. --smoke selects two cases once per model; --repair selects the
11-case targeted regression set twice. A persistent local ledger under
test-results/brief-live-budget.json reserves a conservative cost before each
call and stops at USD 5. Preserve it across runs. This is an experiment limit,
not an account-wide or application-traffic spending control. Recorded evidence
distinguishes the original full comparison from the targeted prompt repair.
Previous results keep their original conditions while you edit or wait for a new selection, and survive a failed request. Date-only submissions explain changes using catalogue busy marks and price/ID order. For example, changing October 1 to October 6 replaces HK-75012 with HK-29829 because of starting-price order; HK-75012 still has no busy mark. Сбросить restores supported initial defaults, clears results and optional conditions, and cancels pending work. Blank optional values do not filter results; supplied duration accepts positive fractional hours. All categories remain selectable in every city, including combinations that correctly produce an empty catalogue outcome.
The compact conditions panel can be collapsed without losing entered values. On mobile, К результатам moves to the result heading and collapses conditions; Условия reopens the form. Requests finishing in the background never move keyboard focus or scroll the page. Selected language and duration remain visible in the closed Дополнительные условия summary.
Contractor explanations, reasons for fewer than three results, both empty outcomes and date changes stay visible. Как получился этот список below the cards expands the price-order rule and detailed exclusion counts. A pending request is distinguished from further edits that have not been submitted.
How it works
The app is one Next.js process serving both the browser interface and HTTP API. The catalogue is loaded into memory on first use and retained for the process.
flowchart LR
Form["Browser form"] --> API["Server: validate request"]
Form --> Parse["Optional wishes: OpenAI interpretation"]
Parse --> Review["User reviews conditions"]
Review --> API
CSV["CSV catalogue in memory"] --> Rules["Hard eligibility rules"]
API --> Rules
Rules --> Order["Confirmed wishes: local evidence order; otherwise price / ID"]
Index["Bundled validated wishes evidence"] --> Order
Order --> Selected["Up to three contractors"]
Selected --> Evidence["Without wishes: optional OpenAI source quotes"]
Selected --> Result["Local explanation assembly"]
Evidence --> Check["Validate quotes against source descriptions"]
Check --> Result
Result --> Cards["Cards and explanation-mode label"]
- The form loads available options from
GET /api/catalog/optionsand sends conditions toPOST /api/recommendationsonly on submission. - The server validates the fields and supported date range. Selection first narrows the catalogue by city and category, then excludes busy contractors, prices above the budget and unsupported event formats. The backend also supports the form's optional language and duration filters.
- Without wishes, eligible profiles are sorted by starting price and catalogue ID. Confirmed wishes instead use the versioned source-evidence rule described above across all eligible profiles before choosing three. AI interpretation is reviewed by the user; hard eligibility and final ordering remain local.
- Without wishes, if configured, one bounded OpenAI request receives the conditions and evidence fields for only the selected profiles. It selects short quotes from their descriptions. Local code checks the response structure, profile IDs, quote length and literal correspondence with the source before using a quote.
- Local code assembles explanations from catalogue facts and accepted quotes. Missing configuration, provider failure or rejected quotes lead to catalogue-only or mixed explanations with a visible label. The underlying selection continues to use the supplied CSV. This is not a synthetic test fixture substituted for the catalogue.
POST /api/brief interprets text separately. The reviewed result is submitted as
optional brief to the recommendation operation; its cards include briefAdvice
and mode brief_evidence. This path uses bundled validated source assertions,
not another provider call. See the public types in contracts/brief.ts and
contracts/contractor-selection.ts and the
immutable v2 handoff package.
Stack and repository layout
All package versions are pinned in package.json and the lockfile.
| Dependency / tool | Role |
|---|---|
| Next.js 16.3.6 | Web application and server routes in one process |
| React / React DOM 19.3.0 | Browser form and result cards |
| TypeScript 6.0.3 | Typed contracts and application code |
| csv-parse 7.0.2 | Decode the supplied CSV catalogue |
| Tailwind CSS 4.3.3 / PostCSS 8.5.28 | Installed styling toolchain |
| Zod 4.6.5 | Available validation dependency; current request rules are implemented in server code |
| Node.js test runner | Runs the checks wired to npm test |
| Playwright 1.63.0 | Browser verification tooling |
| ESLint 9.39.5 / Vitest 5.0.1 | Installed development tooling; no lint script is currently defined, and npm test uses Node's runner |
OpenAI is called through the existing server HTTP adapter; no OpenAI SDK or additional package installation is required. ESLint remains pinned to major 9 for compatibility with the current Next.js lint configuration.
| Path | Responsibility |
|---|---|
src/app/ | Page entry point, layout, styles and API routes |
front/ | Interactive form, loading/error/empty states and result cards |
contracts/ | Public request/response types and labelled examples |
back/catalog/, back/domain/ | CSV loading, calendar and deterministic selection rules |
back/recommend/, back/ai/ | Explanation assembly, quote validation and OpenAI transport |
back/http/, back/composition.ts | Request validation, HTTP responses and module wiring |
back/config/ | Server-only secrets reader |
raw/dataset.csv | Supplied runtime catalogue |
scripts/ | Verification and optional operator utilities |
domain/, architecture/, openspec/ | Source requirements, architecture and implementation evidence |
Checks and troubleshooting
After npm ci, run these from the root:
npm ls --depth=0
npm run typecheck
npm test
Expected: required dependency versions are installed, type checking succeeds,
and secrets, transport, contract, slice, catalogue, selection, evidence,
backend-configuration and frontend-comparison checks pass. These tests use controlled credentials and
transport where needed; no .env, provider account, billable call or Playwright
browser installation is required. They do not replace the browser scenario or
live explanation-quality review. npx playwright install is optional for
browser-test tooling, not an application prerequisite.
| Symptom | Action |
|---|---|
node or npm is not recognized | Install Node.js with npm, reopen the terminal and check the versions. |
Missing csv-parse, UNMET DEPENDENCY, or stale/extra packages | Stop this checkout's server and run npm ci in the root, then repeat the dependency check. Keep the committed lockfile. |
| Start reports a missing production build | Run npm run build successfully before npm start. |
| Port is already in use | Choose another port with -- --port 3102 and open the corresponding URL. |
| Date is rejected | Use a date between 2026-09-23 and 2026-12-31, inclusive. |
| Catalogue is unavailable | Ensure raw/dataset.csv exists and is intact, run from the root, then restart the server after repair. Failed catalogue loads are retained until restart. |
| Explanations say they were generated without AI | This is a supported result. For live quotes, check the OpenAI setting, account/model access and network. Rejected quotes also fall back to catalogue facts. Restart after configuration changes. |
Optional live domain check, after configuring OpenAI:
node scripts/slice/live.mjs
This makes one billable dense-domain request and prints public output and sanitized evidence metadata. Full live acceptance also needs manual review of source correctness, relevance and distinctiveness. A fallback is not a live quality pass.
Limitations and verification status
- The supplied calendar covers 23 September–31 December 2026 only.
- Prices are starting prices. A date without a busy mark is not a confirmed booking; availability and final conditions must be checked with the contractor.
- Synthetic/anonymized profiles and imputed city/price values are labelled.
- There are no booking, messaging or saved-search features, no persistent user data, and no AI quality ranking.
- Date comparisons apply only when the date changes and all other normalized conditions, catalogue version and selection policy are unchanged. Only the latest successful result is kept in memory; reset or reload clears it.
- Catalogue/configuration changes require a server restart.
Recorded acceptance belongs to the linked revisions and scopes, not to every future checkout. Final live rendered-text decisions, three-request timings, clean-checkout evidence and exact publication status are in the P07 delivery task card. Repository publication is separate from submission to the organizer; no organizer submission is performed by the run instructions.
To reproduce the final live timing series after the production build, stop any
server on port 3107 and run node scripts/delivery/live.mjs with local OpenAI
configuration. It starts and stops its own production server and makes exactly
three application submissions (dense, rare, dense); normal provider retry policy
still applies. It requires the Playwright tooling already pinned in the lockfile
and Microsoft Edge installed locally. Results and public screenshots are written
under ignored test-results/p07-live. It fails on live fallback; per-card semantic
quality still needs review against the source. This optional acceptance tooling
is not required to use the application and incurs paid API usage.
| Area | Requirements and recorded evidence |
|---|---|
| Foundation and public contracts | P00 task card |
| First working browser scenario and clean build/start | P01 task card |
| Catalogue loading | P02 task card |
| Selection rules | P03 task card |
| Validated AI evidence | P04 task card |
| Connected backend and frontend handoff | P05 task card |
| Complete frontend flow and date comparison | P06 task card |
For product context, start with domain documentation. See architecture for design and OpenSpec for requirements and current task evidence. The module proposal sequence preserves the original planning order; it is not an instruction to reimplement completed modules.
Configuration details
Requires Node.js 24.4.1 or compatible Node.js 24. These commands need no additional packages. Run from the repository root:
node scripts/secrets/setup.mjs
This creates one root .env from .env.example. An existing
.env is left unchanged. Open the file locally and fill the settings you use:
| Setting | Purpose | Required when |
|---|---|---|
OPENAI_API_KEY | OpenAI API credential | A server operation uses OpenAI |
OPENAI_MODEL | Optional model override; blank uses gpt-5.6-luna | Only to override the default OpenAI model |
APP_PORT | Docker Compose host port; blank uses 3101 | Only to override the Compose port; ignored by npm start |
For the direct Node.js secrets reader, quote values containing # or
whitespace. Values are literal; references such as ${OTHER_VARIABLE} are
not expanded. Docker Compose uses its own interpolation rules: single-quote
literal values containing $ or #, as described in the Docker section.
Add future secrets to the same file and document their empty entries in the
template.
For this application, check only the OpenAI key if using live quotes:
node scripts/secrets/check.mjs OPENAI_API_KEY
A successful check exits with code 0 and says required settings are present. Missing or blank values exit with code 1 and name the missing settings without printing their values. Running without names shows usage and fails. This checks presence only; it does not validate credentials, contact providers, or connect to a database.
Existing process environment variables take precedence over the file, including blank values (which fail validation). This also supports deployments supplying all settings through their environment without a file. The scripts locate the root file relative to their own location, independently of the working directory.
Keep the filled .env out of Git, public archives, screenshots, logs, and
browser code. Git ignores .env and its variants but cannot prevent a forced
add. The file is plaintext: restrict local access. Organizers can use their own
credentials. If team credentials are provided, transfer the file separately
through an agreed private channel; never put them in a repository or public link.
Only the empty template belongs in the repository.
Server integration reference for developers
The server composition calls the reader before external operations and passes only the required values to the corresponding adapters:
import { loadSecrets } from './back/config/secrets.mjs';
const { OPENAI_API_KEY } = loadSecrets({ required: ['OPENAI_API_KEY'] });
// Pass the value to the server adapter; never log or return it to a client.
The import above is relative to a server entry point in the repository root.
The reader returns a frozen object containing only the requested names; it
does not mutate process.env. Errors have a stable code, a safe message,
and a requestId. Browser modules must not import back/config/; names
starting with NEXT_PUBLIC_ are rejected. The P01 task card records application integration separately from the original secrets checks.
Run the focused checks with synthetic temporary credentials:
node --test scripts/secrets/secrets.test.mjs
Current requirements and verification status: OpenSpec tasks.
OpenAI server adapter reference for developers
Requires Node.js 24 and a funded OpenAI API project with model access. No SDK,
GPU or additional npm installation is required for this module. Run the secrets
setup above and fill OPENAI_API_KEY privately. OPENAI_MODEL is optional;
absent or blank selects gpt-5.6-luna. Luna and Terra use reasoning.effort: none
for this extraction task. Both were tested with the current strict schema.
node scripts/openai/check.mjs
node --test scripts/openai/contract.test.mjs
The first command makes one billable live request with a fixed benign prompt.
Expected: JSON with ok: true, mode: live, the model, token usage and duration.
It prints neither the key nor provider response contents. The second command
uses synthetic credentials and a local test server; it incurs no provider cost.
From a server module at the repository root:
import { createOpenAIFromEnv } from './back/ai/openai.mjs';
const openai = createOpenAIFromEnv();
const result = await openai.generate({
input: 'Summarize the supplied text.',
instructions: 'Use only facts from the input.',
maxOutputTokens: 450,
// signal: callerAbortSignal,
});
// Use result.text; do not log private inputs or secrets.
generate also accepts developer-controlled format: { name, schema } for
strict JSON-schema output. The caller must parse and validate domain data.
Results include text, provider, mode, model, responseId, requestId
and optional usage with input/output/total token counts. Errors have a safe
message, stable code and requestId. Codes distinguish configuration/input,
authorization, rate/quota limits, rejected requests, provider unavailability,
invalid/incomplete/refused responses, network failures, timeout and cancellation.
The live check exits nonzero and prints the code on failure.
Calls use Responses with store: false, a six-second deadline including body
consumption, caller cancellation, zero retries and no fallback. Local cancellation
does not guarantee cancellation of provider billing. Never import this Node-only
module into browser components. This verifies transport access; application
routes, contractor selection and UI integration are recorded separately in the P01 task card.
See adapter requirements and evidence.
Авторы коммитов
История коммитов
- 2026-09-23 17:50fixRamazan Amanbayev
- 2026-09-23 17:48fixRamazan Amanbayev
- 2026-09-23 17:48Merge branch 'codex/ui-upgrade'Ramazan Amanbayev
- 2026-09-23 17:47fixRamazan Amanbayev
- 2026-09-23 17:45Record report-only hold preserving concurrent README editRamazan Amanbayev
- 2026-09-23 17:44Record Join City refresh publicationRamazan Amanbayev
- 2026-09-23 17:43Record combined Join City refresh acceptanceRamazan Amanbayev
- 2026-09-23 17:40Merge published brief matching into Join City refreshRamazan Amanbayev
Все коммиты (104)
- 2026-09-23 17:39Apply Modern Join City visual refreshRamazan Amanbayev
- 2026-09-23 17:39Record integrated AI wishes and completed acceptanceRamazan Amanbayev
- 2026-09-23 17:38Merge remote-tracking branch 'origin/main' into codex/ai-upgradeRamazan Amanbayev
- 2026-09-23 17:37fixRamazan Amanbayev
- 2026-09-23 17:36Record clean-checkout acceptance and v2 package pinRamazan Amanbayev
- 2026-09-23 17:35Publish immutable v2 brief API handoff and merged evidenceRamazan Amanbayev
- 2026-09-23 17:32Document AI wishes and reconcile verified reset behaviorRamazan Amanbayev
- 2026-09-23 17:27Merge remote-tracking branch 'origin/main' into codex/ai-upgradeRamazan Amanbayev
- 2026-09-23 17:26Record join city branding integration into mainRamazan Amanbayev
- 2026-09-23 17:24Merge commit '3be31e044da2f72cf30a16a078395c7169738ad6' into codex/ai-upgradeRamazan Amanbayev
- 2026-09-23 17:24Record branding publication and combined integration handoffRamazan Amanbayev
- 2026-09-23 17:24Merge remote-tracking branch 'origin/main' into codex/ai-upgradeRamazan Amanbayev
- 2026-09-23 17:24Update project branding and description to join cityRamazan Amanbayev
- 2026-09-23 17:21Implement confirmed AI wishes and grounded contractor matchingRamazan Amanbayev
- 2026-09-23 17:17Record UI upgrade acceptance and main publicationRamazan Amanbayev
- 2026-09-23 17:10Improve contractor selection presentation and mobile UXRamazan Amanbayev
- 2026-09-23 17:10Merge remote-tracking branch 'origin/main' into codex/ui-upgradeRamazan Amanbayev
- 2026-09-23 17:09Add source-verified brief index and deterministic matchingRamazan Amanbayev
- 2026-09-23 17:09remove nvidiaRamazan Amanbayev
- 2026-09-23 17:08Add source-verified brief index and deterministic matchingRamazan Amanbayev
- 2026-09-23 17:03docs(nvidia): record second hosted failure and input fitRamazan Amanbayev
- 2026-09-23 17:03Specify confirmed brief matching and public contractsRamazan Amanbayev
- 2026-09-23 17:01feat(nvidia): select authorized Nemotron reranking modelRamazan Amanbayev
- 2026-09-23 16:59docs(nvidia): record feature evidence publicationRamazan Amanbayev
- 2026-09-23 16:59docs(nvidia): record unavailable model and defer adoptionRamazan Amanbayev
- 2026-09-23 16:57feat(nvidia): define gated semantic ranking and bounded probeRamazan Amanbayev
- 2026-09-23 16:43docs: synchronize and archive completed P07 deliveryRamazan Amanbayev
- 2026-09-23 16:41docs: record P07 clean checkout and final live acceptanceRamazan Amanbayev
- 2026-09-23 16:38docs: record accepted Docker material provenanceRamazan Amanbayev
- 2026-09-23 16:37Merge commit 'f1251578b2c3474c90ab627af0eac46c3b211f03' into codex/cs-07-deliveryRamazan Amanbayev
- 2026-09-23 16:37test: add P07 live browser timing and delivery guideRamazan Amanbayev
- 2026-09-23 16:37docs: record material and dependency provenanceRamazan Amanbayev
- 2026-09-23 16:36Merge remote-tracking branch 'origin/main' into codex/cs-07-deliveryRamazan Amanbayev
- 2026-09-23 16:35docs: plan approved P07 integration and deliveryRamazan Amanbayev
- 2026-09-23 16:35docs: record Docker delivery publication and P07 handoffRamazan Amanbayev
- 2026-09-23 16:34docs: record clean Docker candidate acceptanceRamazan Amanbayev
- 2026-09-23 16:31fix(docker): include pinned frontend examples during buildRamazan Amanbayev
- 2026-09-23 16:29Merge remote-tracking branch 'origin/main' into codex/deploy-dockerRamazan Amanbayev
- 2026-09-23 16:29feat: add verified Docker Compose launch for judgesRamazan Amanbayev
- 2026-09-23 16:29docs: archive published P06 and synchronize frontend requirementsRamazan Amanbayev
- 2026-09-23 16:26test(front): verify P06 browser flow and document acceptanceRamazan Amanbayev
- 2026-09-23 16:24feat(front): complete resilient selection flow and date comparisonRamazan Amanbayev
- 2026-09-23 16:20docs: plan approved P06 frontend selection flowRamazan Amanbayev
- 2026-09-23 16:14p06Ramazan Amanbayev
- 2026-09-23 16:11upd readmeRamazan Amanbayev
- 2026-09-23 16:07docs: archive accepted P05 handoff and synchronize specsRamazan Amanbayev
- 2026-09-23 16:05docs: record accepted P05 candidate and frontend pinRamazan Amanbayev
- 2026-09-23 16:04docs: publish immutable contractor frontend contract v1Ramazan Amanbayev
- 2026-09-23 16:02test: verify production backend lifecycle and rehearsalsRamazan Amanbayev
- 2026-09-23 16:01test: verify connected configuration fallback and safe faultsRamazan Amanbayev
- 2026-09-23 16:00docs: pin P05 worker assignment and initial acceptanceRamazan Amanbayev
- 2026-09-23 15:59test: expose narrow configured evidence factory seamRamazan Amanbayev
- 2026-09-23 15:59docs: plan bounded backend acceptance and handoffRamazan Amanbayev
- 2026-09-23 15:55add P05Ramazan Amanbayev
- 2026-09-23 15:49docs: record completed P03 publicationRamazan Amanbayev
- 2026-09-23 15:49docs: archive accepted P03 selection-domain changeRamazan Amanbayev
- 2026-09-23 15:47merge: preserve published P04 evidence acceptanceRamazan Amanbayev
- 2026-09-23 15:47spec: sync and archive accepted P04 evidenceRamazan Amanbayev
- 2026-09-23 15:44merge: preserve published P02 alongside P04 evidenceRamazan Amanbayev
- 2026-09-23 15:44docs: record serialized P03 integration readinessRamazan Amanbayev
- 2026-09-23 15:44test: register P04 checks and record live acceptanceRamazan Amanbayev
- 2026-09-23 15:44merge: preserve P02 catalogue acceptance alongside P03Ramazan Amanbayev
- 2026-09-23 15:43fix(evidence): project selected profile fields explicitlyRamazan Amanbayev
- 2026-09-23 15:43docs: finalize P02 publication evidenceRamazan Amanbayev
- 2026-09-23 15:43spec: synchronize verified selection-domain requirementsRamazan Amanbayev
- 2026-09-23 15:43spec: sync and archive accepted catalogue moduleRamazan Amanbayev
- 2026-09-23 15:42docs: record P03 focused acceptance evidenceRamazan Amanbayev
- 2026-09-23 15:42spec: define bounded P04 evidence acceptanceRamazan Amanbayev
- 2026-09-23 15:42docs: document focused P03 verificationRamazan Amanbayev
- 2026-09-23 15:42Merge commit '1deb854f13aaff67435048f0625966228048a71c' into codex/cs-03-domainRamazan Amanbayev
- 2026-09-23 15:41test(domain): cover deterministic selection acceptance gapsRamazan Amanbayev
- 2026-09-23 15:41test: register focused selection acceptance suiteRamazan Amanbayev
- 2026-09-23 15:41test: register catalogue acceptance and record focused evidenceRamazan Amanbayev
- 2026-09-23 15:40test(catalog): verify source decoding and immutable snapshotsRamazan Amanbayev
- 2026-09-23 15:40docs: plan bounded P03 selection-domain acceptanceRamazan Amanbayev
- 2026-09-23 15:39spec: define bounded P02 catalogue acceptanceRamazan Amanbayev
- 2026-09-23 15:33add proposalsRamazan Amanbayev
- 2026-09-23 15:29Merge branch 'codex/p01-checks'Ramazan Amanbayev
- 2026-09-23 15:27Archive accepted P01 slice and synchronize current requirementsRamazan Amanbayev
- 2026-09-23 15:24Include P01 acceptance checks in documented test commandRamazan Amanbayev
- 2026-09-23 15:24test(slice): verify controlled public contract acceptanceRamazan Amanbayev
- 2026-09-23 15:24Document P01 run method and acceptance evidenceRamazan Amanbayev
- 2026-09-23 15:23test(slice): verify controlled public contract acceptanceRamazan Amanbayev
- 2026-09-23 15:20Implement connected contractor first working sliceRamazan Amanbayev
- 2026-09-23 15:15Plan first working slice with frozen P00 boundariesRamazan Amanbayev
- 2026-09-23 15:06Merge branch 'codex/cs-00-foundation'Ramazan Amanbayev
- 2026-09-23 15:05Merge branch 'codex/p00-integration'Ramazan Amanbayev
- 2026-09-23 15:05add 01Ramazan Amanbayev
- 2026-09-23 15:01docs: record archive publication hold for concurrent P01 workRamazan Amanbayev
- 2026-09-23 15:01docs: sync and archive completed P00 foundationRamazan Amanbayev
- 2026-09-23 14:59feat: establish lightweight P00 contracts and foundationRamazan Amanbayev
- 2026-09-23 14:54docs: add P00 foundation brainstorming designRamazan Amanbayev
- 2026-09-23 14:33docs: record published preparation baseline and verificationRamazan Amanbayev
- 2026-09-23 14:31chore: publish project preparation and modular implementation proposalsRamazan Amanbayev
- 2026-09-23 14:08chore: add project harness configurationRamazan Amanbayev
- 2026-09-23 13:35readmeRamazan Amanbayev
Структура репозитория (241 файлов)
.agents/skills/.openspec-target .agents/skills/openspec-apply-change/SKILL.md .agents/skills/openspec-archive-change/SKILL.md .agents/skills/openspec-explore/SKILL.md .agents/skills/openspec-propose/SKILL.md .agents/skills/openspec-sync-specs/SKILL.md .agents/skills/openspec-update-change/SKILL.md .agents/skills/project-delivery/SKILL.md .brainstorming/2026-09-23-backend-composition-and-handoff-design.md .brainstorming/2026-09-23-brev-gpu-access-design.md .brainstorming/2026-09-23-catalog-module-design.md .brainstorming/2026-09-23-contractor-brief-matching-design.md .brainstorming/2026-09-23-contractor-selection-architecture-design.md .brainstorming/2026-09-23-contractor-ui-upgrade-design.md .brainstorming/2026-09-23-docker-compose-delivery-design.md .brainstorming/2026-09-23-domain-context-design.md .brainstorming/2026-09-23-evidence-module-design.md .brainstorming/2026-09-23-first-working-slice-design.md .brainstorming/2026-09-23-foundation-and-contracts-design.md .brainstorming/2026-09-23-frontend-flow-design.md .brainstorming/2026-09-23-harness-consistency-design.md .brainstorming/2026-09-23-integration-and-delivery-design.md .brainstorming/2026-09-23-join-city-visual-refresh-design.md .brainstorming/2026-09-23-openai-response-adapter-design.md .brainstorming/2026-09-23-selection-domain-design.md .brainstorming/2026-09-23-subagent-context-design.md .brainstorming/2026-09-23-unified-local-secrets-design.md .codegraph/.gitignore .codex/agents/apply.toml .codex/agents/decision.toml .codex/agents/design.toml .codex/agents/research.toml .codex/agents/review.toml .codex/config.toml .dockerignore .env.example .gitignore .proposals/00-foundation-and-contracts.md .proposals/01-first-working-slice.md .proposals/02-catalog-module.md .proposals/03-selection-domain.md .proposals/04-evidence-module.md .proposals/05-backend-composition-and-handoff.md .proposals/06-frontend-flow.md .proposals/07-integration-and-delivery.md .proposals/90-optional-nvidia-experiment.md .proposals/README.md .shared/specs/contractor-selection/README.md .shared/specs/contractor-selection/versions/v1/README.md .shared/specs/contractor-selection/versions/v1/contractor-selection.ts .shared/specs/contractor-selection/versions/v1/examples/README.md .shared/specs/contractor-selection/versions/v1/examples/controlled.json .shared/specs/contractor-selection/versions/v1/examples/real-http.json .shared/specs/contractor-selection/versions/v1/examples/request.json .shared/specs/contractor-selection/versions/v2/README.md .shared/specs/contractor-selection/versions/v2/brief.ts .shared/specs/contractor-selection/versions/v2/contractor-selection.ts .shared/specs/contractor-selection/versions/v2/examples/brief.json .shared/specs/contractor-selection/versions/v2/examples/options.json .shared/specs/contractor-selection/versions/v2/examples/real-http.json .shared/specs/contractor-selection/versions/v2/examples/request.json AGENTS.md Dockerfile README.md THIRD_PARTY.md architecture/README.md architecture/implementation-and-verification.md architecture/selection-and-explanations.md architecture/system.md back/ai/brief/interpret.test.mjs back/ai/brief/interpret.ts back/ai/evidence/evidence.test.mjs back/ai/evidence/select.ts back/ai/openai.mjs back/brief/match.test.mjs back/brief/match.ts back/catalog/brief-index.json back/catalog/brief-index.ts back/catalog/catalog.test.mjs back/catalog/load.ts back/composition.ts back/config/secrets.mjs back/domain/date.ts back/domain/select.test.mjs back/domain/select.ts back/domain/types.ts back/http/handlers.ts back/http/normalize.ts back/recommend/ports.ts back/recommend/recommend.ts compose.yaml contracts/brief.ts contracts/contractor-selection.ts contracts/examples/README.md contracts/examples/boundaries.ts contracts/examples/brief.json contracts/examples/fixtures.ts docs/tooling.md domain/README.md domain/acceptance-scenarios.md domain/data-contract.md domain/model-and-rules.md domain/problem-and-scope.md front/BriefAdvice.tsx front/BriefEditor.tsx front/ContractorForm.tsx front/RecommendationResults.tsx front/brief.css front/compareRecommendations.ts front/comparison.test.mjs front/display.ts front/flow.css front/publicResponses.ts openspec/changes/archive/2026-09-23-backend-composition-and-handoff/.openspec.yaml openspec/changes/archive/2026-09-23-backend-composition-and-handoff/design.md openspec/changes/archive/2026-09-23-backend-composition-and-handoff/materialization.json openspec/changes/archive/2026-09-23-backend-composition-and-handoff/proposal.md openspec/changes/archive/2026-09-23-backend-composition-and-handoff/specs/backend-acceptance-and-handoff/spec.md openspec/changes/archive/2026-09-23-backend-composition-and-handoff/tasks.md openspec/changes/archive/2026-09-23-catalog-module/.openspec.yaml openspec/changes/archive/2026-09-23-catalog-module/design.md openspec/changes/archive/2026-09-23-catalog-module/proposal.md openspec/changes/archive/2026-09-23-catalog-module/specs/contractor-catalog/spec.md openspec/changes/archive/2026-09-23-catalog-module/tasks.md openspec/changes/archive/2026-09-23-first-working-slice/.openspec.yaml openspec/changes/archive/2026-09-23-first-working-slice/design.md openspec/changes/archive/2026-09-23-first-working-slice/proposal.md openspec/changes/archive/2026-09-23-first-working-slice/specs/contractor-first-working-slice/spec.md openspec/changes/archive/2026-09-23-first-working-slice/tasks.md openspec/changes/archive/2026-09-23-foundation-and-contracts/.openspec.yaml openspec/changes/archive/2026-09-23-foundation-and-contracts/design.md openspec/changes/archive/2026-09-23-foundation-and-contracts/proposal.md openspec/changes/archive/2026-09-23-foundation-and-contracts/specs/contractor-contract-foundation/spec.md openspec/changes/archive/2026-09-23-foundation-and-contracts/tasks.md openspec/changes/archive/2026-09-23-frontend-selection-flow/.openspec.yaml openspec/changes/archive/2026-09-23-frontend-selection-flow/design.md openspec/changes/archive/2026-09-23-frontend-selection-flow/proposal.md openspec/changes/archive/2026-09-23-frontend-selection-flow/specs/contractor-frontend-flow/spec.md openspec/changes/archive/2026-09-23-frontend-selection-flow/tasks.md openspec/changes/archive/2026-09-23-integration-and-delivery/.openspec.yaml openspec/changes/archive/2026-09-23-integration-and-delivery/design.md openspec/changes/archive/2026-09-23-integration-and-delivery/proposal.md openspec/changes/archive/2026-09-23-integration-and-delivery/specs/contractor-delivery/spec.md openspec/changes/archive/2026-09-23-integration-and-delivery/tasks.md openspec/changes/archive/2026-09-23-selection-domain/.openspec.yaml openspec/changes/archive/2026-09-23-selection-domain/design.md openspec/changes/archive/2026-09-23-selection-domain/proposal.md openspec/changes/archive/2026-09-23-selection-domain/specs/contractor-selection-domain/spec.md openspec/changes/archive/2026-09-23-selection-domain/tasks.md openspec/changes/archive/2026-09-23-validated-ai-evidence/.openspec.yaml openspec/changes/archive/2026-09-23-validated-ai-evidence/design.md openspec/changes/archive/2026-09-23-validated-ai-evidence/proposal.md openspec/changes/archive/2026-09-23-validated-ai-evidence/specs/contractor-evidence/spec.md openspec/changes/archive/2026-09-23-validated-ai-evidence/tasks.md openspec/changes/brev-gpu-access/.openspec.yaml openspec/changes/brev-gpu-access/design.md openspec/changes/brev-gpu-access/proposal.md openspec/changes/brev-gpu-access/tasks.md openspec/changes/contractor-brief-matching/.openspec.yaml openspec/changes/contractor-brief-matching/design.md openspec/changes/contractor-brief-matching/model-comparison.json openspec/changes/contractor-brief-matching/proposal.md openspec/changes/contractor-brief-matching/specs/contractor-brief-matching/spec.md openspec/changes/contractor-brief-matching/specs/contractor-contract-foundation/spec.md openspec/changes/contractor-brief-matching/specs/contractor-frontend-flow/spec.md openspec/changes/contractor-brief-matching/specs/contractor-selection-domain/spec.md openspec/changes/contractor-brief-matching/tasks.md openspec/changes/contractor-ui-upgrade/.openspec.yaml openspec/changes/contractor-ui-upgrade/design.md openspec/changes/contractor-ui-upgrade/proposal.md openspec/changes/contractor-ui-upgrade/specs/contractor-frontend-flow/spec.md openspec/changes/contractor-ui-upgrade/tasks.md openspec/changes/docker-compose-delivery/.openspec.yaml openspec/changes/docker-compose-delivery/design.md openspec/changes/docker-compose-delivery/proposal.md openspec/changes/docker-compose-delivery/specs/docker-compose-delivery/spec.md openspec/changes/docker-compose-delivery/tasks.md openspec/changes/join-city-visual-refresh/.openspec.yaml openspec/changes/join-city-visual-refresh/design.md openspec/changes/join-city-visual-refresh/proposal.md openspec/changes/join-city-visual-refresh/specs/contractor-frontend-flow/spec.md openspec/changes/join-city-visual-refresh/tasks.md openspec/changes/openai-response-adapter/.openspec.yaml openspec/changes/openai-response-adapter/design.md openspec/changes/openai-response-adapter/proposal.md openspec/changes/openai-response-adapter/specs/openai-response-adapter/spec.md openspec/changes/openai-response-adapter/tasks.md openspec/changes/unified-local-secrets/.openspec.yaml openspec/changes/unified-local-secrets/design.md openspec/changes/unified-local-secrets/proposal.md openspec/changes/unified-local-secrets/specs/local-secrets/spec.md openspec/changes/unified-local-secrets/tasks.md openspec/config.yaml openspec/specs/backend-acceptance-and-handoff/spec.md openspec/specs/contractor-catalog/spec.md openspec/specs/contractor-contract-foundation/spec.md openspec/specs/contractor-delivery/spec.md openspec/specs/contractor-evidence/spec.md openspec/specs/contractor-first-working-slice/spec.md openspec/specs/contractor-frontend-flow/spec.md openspec/specs/contractor-selection-domain/spec.md package-lock.json package.json raw/dataset.csv raw/metadata.md raw/proposal.md scripts/backend/configuration.test.mjs scripts/backend/production.mjs scripts/brev/gpu-smoke.py scripts/brev/run.mjs scripts/brev/setup.sh scripts/brief/browser.mjs scripts/brief/cases.json scripts/brief/compare.mjs scripts/brief/contracts.test.mjs scripts/brief/repair-cases.json scripts/contracts/fixtures.test.mjs scripts/delivery/live.mjs scripts/docker/healthcheck.mjs scripts/docker/smoke.mjs scripts/frontend/browser.mjs scripts/frontend/races.mjs scripts/frontend/services.mjs scripts/frontend/unmount.mjs scripts/openai/check.mjs scripts/openai/contract.test.mjs scripts/secrets/check.mjs scripts/secrets/secrets.test.mjs scripts/secrets/setup.mjs scripts/slice/contracts.test.mjs scripts/slice/live.mjs scripts/tooling/check-codegraph.mjs scripts/tooling/check-context7.mjs src/app/api/brief/route.ts src/app/api/catalog/options/route.ts src/app/api/recommendations/route.ts src/app/layout.tsx src/app/page.tsx src/app/style.css src/styles.d.ts tsconfig.json
Ветки (18)
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.