|
1 | | -# Spring Voyage — Project Rules |
| 1 | +# Spring Voyage — project instructions |
2 | 2 |
|
3 | | -A source-available collaboration platform for teams of AI agents — and the humans they work with. General-purpose, domain-agnostic. Built on .NET 10 and Dapr. Namespace: `Cvoya.Spring.*`. |
| 3 | +Spring Voyage is a source-available, domain-agnostic collaboration platform for teams of AI agents and humans. It uses .NET 10 and Dapr under the `Cvoya.Spring.*` namespace. Read [CONVENTIONS.md](CONVENTIONS.md) before changing code. |
4 | 4 |
|
5 | | -## Coding conventions |
| 5 | +The as-built architecture is under [docs/architecture/](docs/architecture/README.md), with accepted decisions under [docs/decisions/](docs/decisions/README.md). Read the relevant pages before working in an area. |
6 | 6 |
|
7 | | -[`CONVENTIONS.md`](CONVENTIONS.md) is mandatory. Read it before writing code. |
| 7 | +## Platform invariants |
8 | 8 |
|
9 | | -## Architecture |
| 9 | +- Agents are Dapr virtual actors with durable per-thread mailboxes. |
| 10 | +- A unit is a composite agent; unit routing is runtime behavior, not platform configuration. See [units vs agents](docs/concepts/units-vs-agents.md). |
| 11 | +- Humans are addressable thread participants, not agents; see [humans](docs/concepts/humans.md). |
| 12 | +- Connectors bridge external systems to units but are not routable subjects. |
| 13 | +- Messages are typed, one-way communications on durable threads. |
| 14 | +- The platform coordinates external agent runtimes in ephemeral or persistent containers; it does not host its own tool loop. |
| 15 | +- Prompt assembly has platform, unit, thread, and agent layers. |
10 | 16 |
|
11 | | -Architecture lives under [`docs/architecture/`](docs/architecture/README.md); decision records under [`docs/decisions/`](docs/decisions/README.md). Read the relevant doc before working on an issue. |
| 17 | +## Build and verification |
12 | 18 |
|
13 | | -Key concepts: |
| 19 | +Use the repository `build`, `test`, `lint`, and `format` skills. Bare `dotnet test SpringVoyage.slnx` can exit successfully without running tests, so never use it as evidence. |
14 | 20 |
|
15 | | -- **Agents** — Dapr virtual actors with a per-thread mailbox. |
16 | | -- **Units** — composite agents that have children. A unit **is** an agent — see [`docs/concepts/units-vs-agents.md`](docs/concepts/units-vs-agents.md) for the quick reference on what's shared vs different; most features apply to both subjects identically. How a unit routes work across its members is runtime behaviour, not platform configuration ([ADR-0053](docs/decisions/0053-units-are-agents-and-one-way-delivery.md)). |
17 | | -- **Humans** — addressable thread participants (not agents). A human implements only `IMessageReceiver` and can be a member of a unit with permission grants. See [`docs/concepts/humans.md`](docs/concepts/humans.md). Portal surfaces for humans are scoped to v0.2. |
18 | | -- **Connectors** — non-routable bridges between external systems and units. |
19 | | -- **Messages** — typed, one-way communications between addressable entities, exchanged on durable threads. |
20 | | -- **Execution** — the platform coordinates external agent runtimes (Claude Code, Codex, Gemini, …) running in containers; it does not host its own tool-use loop. Agents run in one of two hosting modes — _ephemeral_ (a container per turn) or _persistent_ (a long-lived container). |
21 | | -- **Prompt assembly** — four-layer composition: platform, unit context, thread context, agent instructions. |
| 21 | +Install `eng/install-hooks.sh` once per clone. The pre-push hook runs the change-scoped static gate in `eng/ci/ci-local.sh`; hosted CI runs the full build, lint, and test gates. During iteration use focused checks, but API/contract changes still require the solution-wide test gate because integration failures often surface first. A subagent's unqualified “tests pass” means the complete repository test gate passed. |
22 | 22 |
|
23 | | -## Build, test, lint |
| 23 | +Generated-file and migration rules live in [generated files](docs/agent-rules/generated-files.md) and [database migrations](docs/agent-rules/database-migrations.md). Claude loads their matching shims lazily from `.claude/rules/`. |
24 | 24 |
|
25 | | -Use the `build`, `test`, and `lint` commands/skills. Each points at the canonical CI invocation. |
| 25 | +## Source-available extension boundary |
26 | 26 |
|
27 | | -Pitfall: bare `dotnet test SpringVoyage.slnx` exits 0 without running tests. Always go through the `test` command/skill. |
| 27 | +The public repository is the core platform. A private host extends it through dependency injection with tenancy, identity, billing, and premium behavior. Preserve that one-way boundary: |
28 | 28 |
|
29 | | -Shared generated-file and migration guardrails live in [`docs/agent-rules/generated-files.md`](docs/agent-rules/generated-files.md) and [`docs/agent-rules/database-migrations.md`](docs/agent-rules/database-migrations.md). Claude auto-loads matching `.claude/rules/` shims; Codex reads the shared docs through these project instructions. |
| 29 | +- Resolve tenancy through `ITenantContext.CurrentTenantId`; never hardcode `"default"`. Fresh OSS rows use `OssTenantIds.Default`. |
| 30 | +- Tenant-owned persisted entities implement `ITenantScopedEntity`. |
| 31 | +- Services use DI, not static state or ad-hoc singletons. Register replaceable defaults with `TryAdd*`. |
| 32 | +- Extension contracts are public, interface-first, and composable; do not seal intended extension points. |
| 33 | +- `Cvoya.Spring.Core` remains dependency-free. |
| 34 | +- Do not reference private-repository issues, branches, or PRs. |
| 35 | +- New features must remain replaceable or decoratable by a downstream host without forks or patches. |
30 | 36 |
|
31 | | -### Pre-push checks |
| 37 | +Agent runtimes implement `IAgentRuntime`; connectors implement `IConnectorType`. Each ships in its own `Cvoya.Spring.AgentRuntimes.<Name>` or `Cvoya.Spring.Connector.<Name>` project and registers through one DI extension. |
32 | 38 |
|
33 | | -Install the git hook once per clone with `eng/install-hooks.sh`; its pre-push gate runs the fast static checks (`eng/ci/ci-local.sh` — build, format, lint, typecheck for the areas you touched) automatically before every push. CI runs the full `build`, `lint`, and `test` gates on the PR and again in the merge queue, reusing one .NET restore/build for formatting and tests. The resulting `main` push does not repeat ordinary CI, so a regression is caught before merge without a third identical run. |
| 39 | +Operational mutations for runtime, connector, credential, tenant-seed, and skill-bundle administration are CLI-only. The portal may expose read-only visibility. User-facing features still follow the UI/CLI parity rule in [CONVENTIONS.md](CONVENTIONS.md). |
34 | 40 |
|
35 | | -Two things the fast hook does **not** run, so keep them in mind: `test` is solution-wide for a reason — integration tests (`tests/integration/Cvoya.Spring.Integration.Tests`) routinely break first when API contracts change, and a scoped `dotnet test path/to/single.csproj` is no substitute. A dispatched sub-agent's claim of "tests pass" means the full solution-wide `test` gate (e.g. `eng/ci/ci-local.sh --full`), not a project-scoped subset. |
| 41 | +## Documentation |
36 | 42 |
|
37 | | -## Documentation updates |
| 43 | +Architecture pages are living as-built truth. A design-affecting change updates the relevant page and diagrams in the same PR. Feature changes update the relevant guide; new concepts get a `docs/concepts/` entry; decisions get an ADR. Web changes keep `src/Cvoya.Spring.Web/DESIGN.md` synchronized. The concise cross-agent checklist is [documentation](docs/agent-rules/documentation.md). |
38 | 44 |
|
39 | | -[`docs/architecture/`](docs/architecture/README.md) is a **living description of the system** — keep it current the same way ADRs are authored. Any design-affecting change updates the relevant `docs/architecture/` page **and its diagrams** in the same PR; a behavioural or architectural change that leaves those docs stale is not complete. The architecture docs are the single source of truth — these instruction files, and the `docs/concepts/` and `docs/guide/` docs, point at them rather than restating them. |
| 45 | +Release plans live under `docs/plan/<release>/README.md`; use the active release plan as the plan of record. |
40 | 46 |
|
41 | | -When shipping a feature, also update the relevant guide doc, and add a `docs/concepts/` entry for any new concept. The "why" behind a design decision belongs in an ADR under [`docs/decisions/`](docs/decisions/README.md). |
| 47 | +## Workflow |
42 | 48 |
|
43 | | -For changes under `src/Cvoya.Spring.Web/`, keep `src/Cvoya.Spring.Web/DESIGN.md` in sync — it is the portal's visual contract. |
| 49 | +- Work only in a dedicated task worktree under `~/dev/worktrees/spring-voyage/<task>`, based on current `origin/main`; never edit the main checkout. |
| 50 | +- One PR owns one coherent outcome. Combine issues only when instructed or when they are inseparable within one owned surface and verification boundary. |
| 51 | +- Rebase on current `origin/main` before pushing and before merging. |
| 52 | +- Append to shared registries such as `StateKeys`, DI registrations, and enums; avoid reorder churn. |
| 53 | +- File and natively wire genuinely separate follow-ups before the PR lands. |
44 | 54 |
|
45 | | -The documentation quick reference lives in [`docs/agent-rules/documentation.md`](docs/agent-rules/documentation.md) and applies to both Claude and Codex. |
| 55 | +All GitHub writes for this repository, including commits, use `gh-app`. PRs are review-ready by default, use squash merge, and repeat the closing keyword for every completed issue. |
46 | 56 |
|
47 | | -## Source-available platform and extensibility |
48 | | - |
49 | | -This repository is the **public, source-available core** of Spring Voyage. A private repository extends it via git submodule and dependency injection — adding multi-tenancy, OAuth/SSO, billing, and premium features. |
50 | | - |
51 | | -- **Don't bypass `ITenantContext`.** Resolve the current tenant through `ITenantContext.CurrentTenantId` (a `Guid`). Never hardcode the literal string `"default"` and never assume only one tenant exists. The OSS deployment's fresh-install rows are owned by `Cvoya.Spring.Core.Tenancy.OssTenantIds.Default` (a deterministic v5 UUID; see [`docs/architecture/data-and-identity.md`](docs/architecture/data-and-identity.md)) — reference the constant from code, never the literal string. New persisted entities that should be tenant-scoped must implement `ITenantScopedEntity` so the cloud host can enforce isolation through its scoped overrides. |
52 | | -- **Don't make services static or use singletons outside DI.** Everything must go through the container so the private repo can control lifetime and scoping. |
53 | | -- **Don't create internal types that the private repo would need to access.** If a type is part of the extension contract, make it `public`. Use `internal` only for true implementation details. |
54 | | -- **Don't reference private repo issues, PRs, or branches.** It is fine to acknowledge that a Spring Voyage hosted service exists, but do not link to or create dependencies on `cvoya-com/spring` issues or PRs from this repo. The dependency direction is one-way: the private repo may reference this repo's work, not the reverse. |
55 | | -- **Every design decision must account for extensibility.** The private repo extends, overrides, or composes OSS behaviour cleanly — without forking, patching, or working around limitations. Treat this repo as a framework that consumers use. |
56 | | - |
57 | | -### Extension model |
58 | | - |
59 | | -- **Tenant-aware overrides.** The OSS core models tenancy through `ITenantContext` and ships a single-tenant default. The cloud host swaps in a scoped implementation. OSS code must not assume a single tenant or hardcode the default tenant id. |
60 | | -- **DI overrides.** The cloud host replaces OSS service registrations with tenant-aware implementations using `TryAdd*`-friendly registration on the OSS side. |
61 | | -- **Plugin contracts.** Implement `IAgentRuntime` (LLM backend + execution tool + credential schema + model catalogue) or `IConnectorType` (external-system binding) and register via DI; the host picks new implementations up without core changes. |
62 | | -- **Cloud API host.** Layers middleware (auth, tenant context) on top of the OSS API host. |
63 | | - |
64 | | -### Design principles for extensibility |
65 | | - |
66 | | -1. **Interface-first.** Define interfaces in `Cvoya.Spring.Core`, implement in `Cvoya.Spring.Dapr`. Alternative implementations slot in via DI. |
67 | | -2. **`TryAdd*` for DI registrations.** Downstream consumers register their own implementations before calling `AddCvoyaSpring*()`, and OSS registrations don't overwrite them. |
68 | | -3. **Don't seal extensible types.** Services, handlers, strategies, middleware are not `sealed` unless leaf-only. |
69 | | -4. **Composition over inheritance.** Inject collaborators; extend by wrapping or decorating. |
70 | | -5. **No hardcoded single-tenant assumptions.** Use injected services for anything the cloud might scope per tenant. |
71 | | -6. **Virtual hooks on base classes.** Make template methods on `*Base` classes `virtual`. |
72 | | -7. **`Cvoya.Spring.Core` stays dependency-free.** Domain abstractions only — zero NuGet packages. |
73 | | -8. **Extension-point checklist** for new features: |
74 | | - - Can the cloud swap this implementation via DI? → interface. |
75 | | - - Can it extend behaviour? → decorator/wrapper or virtual methods. |
76 | | - - Does it assume a single deployment context? → parameterise via DI. |
77 | | - |
78 | | -### Plugins (agent runtimes and connectors) |
79 | | - |
80 | | -Agent runtimes and connectors are first-class plugins. Each ships as its own `Cvoya.Spring.AgentRuntimes.<Name>` or `Cvoya.Spring.Connector.<Name>` project, references only what its contract demands, and registers via a single `AddCvoyaSpring<Kind><Name>()` DI extension. Host-side code references the abstraction only — the registry, install surface, and bootstrap pick up new plugins automatically. Per-project READMEs document each runtime/connector's contract; see also `CONVENTIONS.md` § "Agent Runtimes and Connectors Are Plugins". |
81 | | - |
82 | | -## Operator surfaces — relaxation of UI/CLI parity |
83 | | - |
84 | | -Operational surfaces (agent-runtime config, connector config, credential health, tenant seeds, skill-bundle bindings) are **CLI-only by design**. The portal MAY expose **read-only** views for visibility, but every mutation goes through the `spring` CLI. |
85 | | - |
86 | | -User-facing features remain strictly parity-bound — see [`CONVENTIONS.md`](CONVENTIONS.md) § "UI / CLI Feature Parity". |
87 | | - |
88 | | -## Agents, Sub-agents, concurrent agents |
89 | | - |
90 | | -Multiple coding agents work on this codebase simultaneously. |
91 | | - |
92 | | -Rules: |
93 | | - |
94 | | -- Every PR must be developed in a dedicated worktree — create one before starting any code work. Never work directly in the main checkout. Other agent processes may be active concurrently and so making changes to the main worktree might result into conflicts or agents tripping on each other's work. |
95 | | -- Small, focused PRs — one issue per PR unless instructed to combine issues into one. |
96 | | -- Rebase onto `main` before merging. |
97 | | -- When adding to shared files (`StateKeys`, DI registrations, enums) — append to the end. |
98 | | -- File follow-up issues before the PR lands and reference concrete numbers in the PR body. Prose-only "we'll file it later" routinely drops follow-ups on the floor. |
99 | | - |
100 | | -## Repository Configuration |
101 | | - |
102 | | -- `.claude/settings.local.json` is gitignored and is the correct place for user-specific tooling (MCP servers, design tools, personal preferences). Do not add user-specific tool configuration to committed repo files (`settings.json`, agent definitions, or CLAUDE.md). Repo-level config should reflect project requirements shared by all contributors, not individual workflow preferences. |
| 57 | +User-specific tools and MCP servers belong in `.claude/settings.local.json` or user configuration, not committed project settings, agent definitions, or this file. |
0 commit comments