Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
APP_NAME=agentic-project-starter
APP_NAME=zurich-tax-desk
APP_ENVIRONMENT=local
APP_HOST=0.0.0.0
APP_PORT=8000
Expand Down
134 changes: 26 additions & 108 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,133 +1,47 @@
# agentic-project-starter
# Zurich Tax Desk

`agentic-project-starter` is a Python boilerplate for new projects that need:
`Zurich Tax Desk` is a chat-first finance app for people living or working in
Zurich who need help preparing tax questions, organizing filing details, and
understanding common personal-tax scenarios before they act.

- a FastAPI runtime
- OpenAI Agents SDK scaffolding
- ETL job structure
- an optional ChatKit-based frontend accelerator
- local and Docker execution
- strong Codex contributor guidance through `AGENTS.md` and repo-local skills
The codebase still uses the starter’s FastAPI runtime, ChatKit frontend, and
package boundaries, but the default product behavior is now a Zurich-focused
tax guidance assistant with explicit guardrails for high-stakes decisions.

This repository intentionally ships placeholder workflows rather than business
logic. The goal is to give new projects a clean, opinionated starting point.
## Product focus

## Use This Template
- Zurich personal tax preparation and orientation
- salary withholding and permit-related questions
- deduction checklists for employees, families, and side-income earners
- document preparation for moves, residency changes, and annual filing
- escalation guidance when users should verify with official sources or a Swiss tax professional

This repository is meant to be used as a GitHub template, not as the final
application repo itself.
## Guardrails

To start a new project:

1. Open this repository on GitHub.
2. Click `Use this template`.
3. Create a new repository with your project name and visibility.
4. Clone the new repository locally.
5. In the new repository, update:
- package and module names if `agentic_project_starter` is no longer the right name
- `.env` values and environment-variable defaults
- README, docs, and runtime settings for the actual project
6. Commit that initial project rename and setup baseline before you start large implementation changes.

Recommended first pass in the new repo:

- decide the real product and user-facing workflow
- define the actual FastAPI routes and domain services
- replace placeholder agent definitions with project-specific agents, tools, and prompts
- replace ETL examples with real sources, transforms, and sinks
- decide whether Docker is enough for your demo or whether your project needs its own deployment setup

## Hackathon Path

For a Codex-focused hackathon, keep the first loop small and demonstrable:

1. Create a repository from this template and clone it.
2. Run `cp .env.example .env`, then add `OPENAI_API_KEY` if your demo needs live OpenAI calls.
3. Run `make setup`, `make doctor`, and `make check` to confirm the scaffold is healthy.
4. Ask Codex to implement one real feature on top of the existing package boundaries.
5. Run `make serve` and, for chat-first demos, `make frontend`.
6. Before opening a PR, run `make quiz` and commit `.change-quiz/result.json`; the quiz check is mandatory by default.

## How To Prompt Codex

After creating a new repo from this template, use Codex to replace the scaffold
incrementally instead of asking for one giant rewrite.

Good prompts usually include:

- the business goal and target users
- the API or CLI behavior you want
- the data sources and storage model
- the runtime target
- constraints such as auth, latency, cost, compliance, or testing expectations
- concrete acceptance criteria

When prompting Codex in the new repo:

- tell it to preserve the overall scaffold unless there is a clear reason to change structure
- ask it to implement one subsystem at a time
- ask it to update tests, docs, and env vars together when behavior changes
- be explicit about what is real logic versus what should stay as reusable template scaffolding
- if you want frontend help, say whether the product is chat-first; this starter has repo-local skills that can recommend ChatKit when that is actually a good fit
- name the relevant repo-local skill when the task is fragile: `scaffold-outcomes` for first product work, `frontend-starter-guidance` for chatbot/UI work, and `architecture-boundaries` for runtime or API wiring

Example prompts for a new repo:

```text
Use the scaffold-outcomes and architecture-boundaries skills.
Implement the real application logic for this project on top of the starter.
Keep the existing runtime/api/shared structure. Add FastAPI routes for account
creation, login, and project management, backed by PostgreSQL. Update tests,
environment variables, and docs as part of the change.
```

```text
Use the scaffold-outcomes skill.
Replace the placeholder OpenAI Agents SDK scaffolding with a real multi-agent
workflow for financial research. Keep the existing agentic package structure.
Add a coordinator, researcher, and report-writer agent, define the tools they
use, and add smoke tests for registry wiring and dry-run behavior.
```

```text
Use the agent-etl-scaffolder and architecture-boundaries skills.
Replace the ETL starter jobs with a real pipeline that ingests CSV files from S3,
normalizes them into a warehouse-friendly schema, and writes outputs to
PostgreSQL. Keep the existing etl package structure, add typed job configs, and
update docs and environment variables.
```

```text
Use the frontend-starter-guidance skill.
Design the frontend for this product on top of the starter. First decide whether
the experience should be chat-first or not. If it is chat-first, you may
recommend the optional ChatKit path in `frontend/`, but keep the backend seams
generic and replaceable. If it is not chat-first, recommend a conventional React
app structure instead. Update docs and runtime wiring only where needed.
```
- The assistant is educational only and does not replace a fiduciary or legal adviser.
- Rates, thresholds, deadlines, and filing positions can vary by year, commune, and user status.
- Users should verify material decisions with the Zurich tax office, their commune, or a qualified Swiss tax professional.

## Quick start

Use this after creating a new repo from the template to validate that the
scaffold still boots correctly before you add real business logic.

```bash
cp .env.example .env
make setup
make doctor
make serve
make frontend
```

API endpoints:
Add `OPENAI_API_KEY` to `.env` before sending live chat messages.

Local endpoints:

- `GET /healthz`
- `GET /v1/runtime/summary`
- `POST /chatkit`

Optional frontend:
Frontend:

- `http://127.0.0.1:5173` during local Vite development
- `http://127.0.0.1:5173` during Vite development
- same-origin frontend bundle in Docker after `docker compose up --build`

## Common commands
Expand Down Expand Up @@ -163,6 +77,10 @@ make docker-up
└── .agents/skills
```

The product-specific chat behavior lives under `src/agentic_project_starter/chat`.
The UI shell lives in `frontend/`. The broader runtime layout remains close to
the starter so future domain work can stay incremental.

## Documentation

- [Architecture](docs/architecture.md)
Expand Down
40 changes: 23 additions & 17 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# Architecture Overview

This repository is intentionally a scaffold rather than a product. It gives new
projects a clear starting point for:
This repository now configures the starter into a chat-first Zurich tax guidance
app while preserving the starter’s package boundaries.

- a Python web runtime
- OpenAI Agents SDK-based orchestration
- ETL pipelines
- an optional ChatKit-based frontend accelerator
The current product focus is:

- Zurich personal tax guidance for educational use
- a ChatKit-based finance chat frontend
- OpenAI Agents SDK orchestration for the assistant persona
- local and Docker execution

## Source layout
Expand All @@ -22,11 +23,11 @@ projects a clear starting point for:
- `src/agentic_project_starter/agentic`
- starter agent registry, request/response models, and OpenAI Agents SDK stubs
- `src/agentic_project_starter/chat`
- generic chat service seam, file-backed starter chat storage, and the optional ChatKit adapter
- Zurich tax assistant profile, chat service seam, file-backed chat storage, and the ChatKit adapter
- `src/agentic_project_starter/etl`
- ETL job registry and starter execution contracts
- `frontend`
- optional Vite + React ChatKit accelerator that can be removed or replaced in downstream repos
- Vite + React ChatKit UI shell for the finance chat experience

## Runtime model

Expand All @@ -35,16 +36,21 @@ projects a clear starting point for:
- FastAPI exposes a minimal service surface:
- `/healthz`
- `/v1/runtime/summary`
- `/chatkit` for the optional self-hosted chat accelerator
- `/chatkit` for the self-hosted chat accelerator

The runtime intentionally exposes starter metadata rather than business logic.
The runtime summary includes assistant metadata and guardrails so tests and
operators can inspect the active product profile.

## Extension path
## Assistant behavior

When turning this starter into a real project:
- The chat coordinator loads product context through a tool before each turn.
- The responder focuses on Zurich tax preparation topics and asks clarifying questions when facts are missing.
- The assistant is instructed to avoid pretending certainty on rates, deadlines, or filing positions.
- High-stakes or ambiguous situations are escalated to official Zurich sources or a qualified Swiss tax professional.

## Extension path

1. Replace placeholder agent registry entries with task-specific agents and tools.
2. Replace ETL stub stages with real extract/transform/load implementations.
3. Replace the file-backed chat storage and ChatKit adapter if your real project needs different UX or persistence.
4. Add domain routers and services under `api/` and `runtime/`.
5. Add project-specific deployment automation only when the project needs it.
1. Add Zurich-specific retrieval or official-source lookup before expanding legal specificity.
2. Replace file-backed chat storage if the app needs multi-user persistence.
3. Add domain routers and services if the product grows beyond chat.
4. Replace ETL stubs only when the app needs ingestion or analytics pipelines.
10 changes: 5 additions & 5 deletions docs/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The application reads settings from `.env` and optional overrides from

| Variable | Default | Purpose |
| --- | --- | --- |
| `APP_NAME` | `agentic-project-starter` | Service name used in API metadata and logs |
| `APP_NAME` | `zurich-tax-desk` | Service name used in API metadata and logs |
| `APP_ENVIRONMENT` | `local` | Runtime profile such as `local`, `docker`, or `ci` |
| `APP_HOST` | `0.0.0.0` | Bind host for local and container execution |
| `APP_PORT` | `8000` | Bind port for local and container execution |
Expand All @@ -18,16 +18,16 @@ The application reads settings from `.env` and optional overrides from
| Variable | Default | Purpose |
| --- | --- | --- |
| `OPENAI_API_KEY` | empty | API key required for live OpenAI calls |
| `OPENAI_MODEL` | `gpt-5` | Default model name used by starter agent definitions |
| `OPENAI_MODEL` | `gpt-5` | Default model name used by the Zurich tax chat assistant |
| `OPENAI_DEFAULT_AGENT` | `coordinator` | Default agent name for project-specific wrappers |
| `OPENAI_ENABLE_TRACING` | `true` | Enables OpenAI Agents SDK tracing when `OPENAI_API_KEY` is configured |
| `CHATKIT_DOMAIN_KEY` | `local-dev` | Domain key passed to the optional self-hosted ChatKit frontend |
| `CHATKIT_DOMAIN_KEY` | `local-dev` | Domain key passed to the self-hosted ChatKit frontend |

## Data and observability

| Variable | Default | Purpose |
| --- | --- | --- |
| `STORAGE_URI` | `file://./var/data` | Starter storage location for local or containerized work |
| `STORAGE_URI` | `file://./var/data` | Storage location for chat history and other local app data |
| `ETL_DEFAULT_DATASET` | `demo-dataset` | Default dataset identifier for CLI ETL commands |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | empty | Optional OTLP endpoint for telemetry export |

Expand All @@ -37,5 +37,5 @@ The application reads settings from `.env` and optional overrides from
- `.env`: local developer secrets and overrides
- `.env.local`: optional higher-priority local overrides
- `.env.docker.example`: example container-oriented overrides
- `frontend/.env.example`: optional frontend overrides for the ChatKit accelerator
- `frontend/.env.example`: optional frontend overrides for the ChatKit UI
- `frontend/.env.local`: local frontend overrides such as `VITE_API_BASE_URL` when the backend is not on `127.0.0.1:8000`
18 changes: 12 additions & 6 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ cp .env.example .env
make setup
```

Add your `OPENAI_API_KEY` to `.env` if you plan to enable live agent runs.
Add your `OPENAI_API_KEY` to `.env` if you plan to enable live Zurich tax chat responses.

## 2. Run locally

Expand All @@ -17,11 +17,17 @@ make serve
make frontend
```

The backend stays available on `http://127.0.0.1:8000`. The optional ChatKit
frontend runs on `http://127.0.0.1:5173` and talks to the starter’s self-hosted
`/chatkit` endpoint through the Vite dev proxy. Add `OPENAI_API_KEY` to `.env`
before sending live chat messages. The frontend template already includes the
required ChatKit web component loader in `frontend/index.html`.
The backend stays available on `http://127.0.0.1:8000`. The chat-first frontend
runs on `http://127.0.0.1:5173` and talks to the self-hosted `/chatkit`
endpoint through the Vite dev proxy. Add `OPENAI_API_KEY` to `.env` before
sending live chat messages. The frontend template already includes the required
ChatKit web component loader in `frontend/index.html`.

Suggested first prompts:

- "I moved to Zurich this year. What documents should I gather for my first tax return?"
- "I am a salaried employee in Zurich. Which deductions should I review before filing?"
- "I pay withholding tax in Zurich. What facts matter before I decide whether to seek professional advice?"

## 3. Run placeholder workflows

Expand Down
2 changes: 1 addition & 1 deletion frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Starter Chat</title>
<title>Zurich Tax Desk</title>
<script src="https://cdn.platform.openai.com/deployments/chatkit/chatkit.js" async></script>
</head>
<body>
Expand Down
21 changes: 21 additions & 0 deletions frontend/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe("App", () => {
render(<App />);

expect(screen.getByTestId("chatkit")).toBeTruthy();
expect(screen.getByText("Zurich Tax Desk")).toBeTruthy();
});

it("uses the same-origin ChatKit endpoint by default", () => {
Expand All @@ -38,4 +39,24 @@ describe("App", () => {
}),
);
});

it("configures the Zurich tax guidance prompts and placeholder", () => {
render(<App />);

expect(chatKitMock.useChatKit).toHaveBeenCalledWith(
expect.objectContaining({
composer: expect.objectContaining({
placeholder:
"Ask about Zurich tax returns, deductions, residency, or withholding tax…",
}),
startScreen: expect.objectContaining({
greeting: "Zurich tax guidance for real-world filing prep",
prompts: expect.arrayContaining([
expect.objectContaining({ label: "First tax return" }),
expect.objectContaining({ label: "Employee deductions" }),
]),
}),
}),
);
});
});
Loading
Loading