

# Add a fact [#add-a-fact]

Use this guide when you need a new input fact, derived fact, parameter fact or
question prompt for a calculator.

## Before you start [#before-you-start]

* Find the owning package in
  [Package ownership](https://github.com/crcorbett/taxkit/blob/a151e51e8a30247526fa93412df046955846eca4/docs/architecture/package-ownership.md).
* Read the current fact model in
  [Facts](https://github.com/crcorbett/taxkit/blob/a151e51e8a30247526fa93412df046955846eca4/docs/architecture/facts.md).
* Check whether an existing fact descriptor already covers the tax behaviour.

Shared primitives, `FactId` and common descriptor contracts live in
`@taxkit/core`. Rule-specific facts should live in the rule package that owns
the rule, parameter table and golden tests.

## Proposal [#proposal]

Describe the fact before you code:

* whether it is an input fact, derived fact or parameter fact
* the source that proves the fact is tax-significant
* the schema-backed value and branded fields it needs
* the `Context.Service` that provides the value
* the descriptor title, authority policy and question metadata
* which calculators will accept or derive it

Do not mirror canonical fields such as `id: string` in a local DTO. Import the
owning `FactId`, schema, descriptor, constructor or service tag.

## Implementation [#implementation]

Define the fact in the owning package and keep the descriptor beside the
package-owned rule or scenario input that uses it.

This example shows the existing gross-pay fact. For a new fact, use the
name, schema and service key owned by its rule package.

```ts
import { Money } from "@taxkit/core/primitives";
import { PayPeriod } from "@taxkit/rules-au-pay/facts";
import { Context, Schema } from "effect";

// This illustrates the existing fact definition. A new fact needs its own
// rule-owned name, schema, service key and descriptor.
export class GrossPay extends Schema.TaggedClass<GrossPay>()("GrossPay", {
  amount: Money,
  period: PayPeriod,
}) {}

export class GrossPayFact extends Context.Service<GrossPayFact, GrossPay>()(
  "taxkit/rules-au-pay/fact/GrossPay"
) {}
```

Use schema-owned optional fields with `Option` and `Match` when the fact has
optional policy. Do not create default values that change a tax outcome, such
as assuming a HELP/STSL debt answer or a tax-free threshold claim.

## Tests [#tests]

Your evidence should include:

* schema decode tests for valid and invalid fact values
* type tests when the fact changes a public SDK or calculator input type
* graph validation evidence showing the fact is provided and required by the
  expected rules
* golden tests when the fact changes a tax calculation result
* API compatibility tests when the fact appears in `CalculatorRunRequest`
* SDK compatibility tests when SDK helpers accept the fact

## Pull request evidence [#pull-request-evidence]

Link the source citation for the fact, list the owning package, show the test
commands you ran and explain compatibility impact in
[PR evidence checklist](/contributing/pr-evidence-checklist).
