# Getting started with Objective Guard

This guide takes you from an unmodified Git repository to a first closed round with exported evidence.

## What Objective Guard does

Objective Guard is a continuous-verification runtime for coding agents. It returns deterministic feedback at edit time over four narrow universal detectors, freezes intention into a typed task contract (objective, round, key result, scope, non-goals), reports drift signals between the declared contract and the observed change surface, and persists reproducible evidence when a round closes.

It is a functional alpha for JavaScript Git repositories. The universal detectors are deliberately narrow: newly disabled or weakened tests, new production stubs, new dependencies, and JavaScript syntax errors in `.js`, `.mjs`, and `.cjs` files. Everything beyond that comes from checks you declare yourself.

## Requirements

- Node.js 20 or later (`engines.node` is `>=20`). No runtime dependencies.
- A Git repository with at least one commit. Session and baseline commands resolve `git rev-parse HEAD`, so an empty repository cannot open a round.
- Git available on `PATH`. Every surface, diff, and baseline measurement is a `git` call.

The public package declares `oguard` for the CLI and `oguard-hooks` for harness setup. A global install exposes both commands.

Throughout this guide, `oguard` means either the installed binary or `node /path/to/objective-guard/src/cli.mjs`. The CLI resolves the target repository from the current working directory through `git rev-parse --show-toplevel`, so invoking a clone's `src/cli.mjs` from inside another repository operates on that other repository.

## Install

In this repository, invoke the CLI directly and confirm the command surface:

```bash
node src/cli.mjs --help
node src/cli.mjs task-flow --json
npm test
```

`--help` exits with code 2 and prints the full usage on stderr; that is expected. `npm test` runs `node --test test/*.test.mjs`, which builds temporary repositories and exercises the end-to-end flow.

In any other Git repository, choose one of these forms:

```bash
# Option A — no installation; run the clone's CLI from inside the target repository
cd /path/to/your-repository
node /path/to/objective-guard/src/cli.mjs task-flow --json

# Option B — install from the local clone so that oguard is on PATH
npm install --global /path/to/objective-guard
cd /path/to/your-repository
oguard task-flow --json

# Option C — install the public package and configure both supported harnesses
npm install --global @fyso/objective-guard
oguard-hooks install --harness all
cd /path/to/your-repository
oguard init
```

`oguard-hooks install --harness claude|codex|all` merges Objective Guard's hooks into the user-level harness configuration without duplicating its own entries. Use `--dry-run` to inspect the target configuration paths first. It never creates a task contract; run `oguard init` from each repository you want to govern.

## Your first round

Run this sequence in a Git repository with at least one commit and a clean working tree. The example opens a `feature` round for a checkout objective, scoped to `src/**` and `test/**`.

1. **Create the placeholder contract.**

   ```bash
   oguard init
   ```

   Writes `oguard.contract.json` with a placeholder objective (`O-UNSPECIFIED`) and `policy.goal_substitution=observe`. It refuses to overwrite an existing contract. The placeholder is an on-ramp, not a valid configuration: it does not grant preflight and it does not let the pre-edit gate pass.

2. **Draft a revisable intent.**

   ```bash
   oguard intent --objective-id O-CHECKOUT --objective "Protect checkout" \
     --task-type feature --scope "src/**,test/**"
   ```

   Saves objective, task type, and candidate scope to `.oguard/intent.json`. This is local state, not a contract. `intent` is only available while the contract is still the placeholder; once a typed contract exists it is rejected, because a change of direction must be an explicit successor round.

3. **Resolve the candidate scope.**

   ```bash
   oguard intent-scan
   ```

   Prints `Scanned N candidate files`. The scan matches the globs against Git-observable files only (tracked plus untracked-not-ignored) and lists test candidates separately. It interprets no semantics and grants no preflight.

4. **Review the draft before freezing it.**

   ```bash
   oguard intent-status --json
   ```

   Shows the draft, its hash, and whether the contract is `not-configured`, `placeholder`, or `typed`. If the scope resolved to the wrong files, run `oguard intent` again with corrected globs and rescan. Iterating here is free; iterating after the contract is frozen is not.

5. **Freeze the round with reproducible checks.**

   ```bash
   oguard start --from-intent \
     --round-id R-1 --round "Add checkout regression coverage" \
     --kr-id KR-1 --kr "Checkout has a regression test" \
     --check test=npm,run,test --check-timeout-ms 900000 \
     --criterion-id C-1 --criterion "Checkout behavior and compatibility pass" \
     --criterion-checks test \
     --obligation behavior=C-1 --obligation compatibility=C-1
   ```

   `--from-intent` copies objective, task type, and scope from the reviewed draft; round and key result must be supplied here. `start` writes the typed contract, always includes `oguard.contract.json` in the scope, and freezes an immutable snapshot in `.oguard/session-baseline.json`. It refuses a second call: successors go through `next-round`.

   The check, criterion, and obligation flags matter for a `feature` round. A `feature` requires `behavior` and `compatibility` obligations backed by criteria that reference declared checks. Omit them and `start` prints `W criterion-checks — …`; the round can then only end with those obligations `unknown`, and a frozen round cannot be amended — manually executed test results are never credited retroactively. The remedy in that case is a successor round with the checks declared, not an edit of the current evidence.

   `--check test=npm,run,test` declares the argv `npm run test`. Because that matches what the planner discovers from `package.json`, the discovered candidate is deduplicated instead of being reported as an unconfigured recommendation.

6. **Commit the contract.**

   ```bash
   git add oguard.contract.json && git commit -m "open round R-1"
   ```

   `oguard.contract.json` (and `oguard.plan.json`, `oguard.kpis.json` when used) are versioned task artifacts. `.oguard/` is local, append-only operational state; add it to `.gitignore`.

7. **Run the preflight before the first material edit.**

   ```bash
   oguard task-flow --json
   ```

   `status: "ready"` summarizes objective, round, task type, work context, scope, non-goals, baseline state, governance health, and KPI validation, and records a preflight authorization bound to the contract hash and the KPI policy hash for one hour. `action-required` exits non-zero and names what to resolve first. A fresh repository has no `oguard.kpis.json`, so KPIs report `not-configured`; versioning that file makes the KPI policy part of preflight.

8. **Make the edit inside scope.** For example, add `test/checkout.test.mjs` and change `src/checkout.mjs`.

9. **Check the edited file.**

   ```bash
   oguard check --file test/checkout.test.mjs
   ```

   Applies the four universal detectors to that file's added lines. It prints only new findings (at most five, then a count) plus the number of resolved ones, and exits 1 when any finding has severity `error` — a disabled test, a production stub, or a syntax error. A new dependency is a `warning` and does not change the exit code.

10. **Check alignment against the contract.**

    ```bash
    oguard align
    ```

    Reprints objective and round, the change surface (`files / churn / modules / outside scope`), per-key-result evidence counts, and at most one alignment warning. The exit code stays 0 even when it warns. A touched path is candidate evidence, never proof that a key result is achieved.

11. **Close the round.**

    ```bash
    oguard round-end
    ```

    The default `fast` mode runs the universal detectors over every changed source file, the declared checks scoped to `fast` (or to no mode), `git diff --check`, and then evaluates criteria and obligations. Read the final line: `[round-end] STATUS — N changed source files; verification=…; evidence=…`. `pass` means the checks in scope found no violation; `warn` means an active finding or an effective alignment warning; `block` means a deterministic failure or a violated required criterion; `unknown` means the evidence was insufficient to conclude.

12. **Close with the final gate.**

    ```bash
    oguard round-end --mode final
    ```

    Selects the checks declared for `final` (or for no mode) and blocks when any required criterion is still `unknown`. This is the mode that answers whether the round is promotable.

13. **Export the evidence.**

    ```bash
    oguard evidence export --output evidence/runs/first-round.json
    ```

    Writes the sanitized evidence artifact. The output path must match `evidence/runs/*.json`; any other location is rejected. Optionally record human adjudication afterwards with `oguard outcome --outcome confirmed-correct --note "Reviewed against expected behavior"`, which appends an event without changing the recorded verdict.

## What the guard checked — and what it did not

- `pass` means only that the checks in scope found no violation. It is not evidence that the objective was achieved, that the change is correct, or that the guard was useful.
- A skipped candidate check never widens a `pass`. It stays `skipped`, and the close reports `verification: "incomplete"` with the skipped names. Two reasons are advisory and do not degrade the verdict: `discovered-not-configured` (a `package.json` script the planner found but you never authorized) and `workspace-affected-not-configured`.
- `unknown` means insufficient evidence, not failure. In `fast` mode a required criterion left `unknown` keeps the verdict at `unknown`; the `bugfix` regression obligation blocks even in `fast`. In `final` mode every required `unknown` criterion blocks.
- Obligations are never inferred. A `feature` obligation is `proven` only when every criterion it references was proven by a declared check; paths, objective text, and model judgment do not count.
- Alignment counts paths, not meaning. Accounting files (`oguard.contract.json`, `oguard.plan.json`, `oguard.kpis.json`, `.oguard/**`) never credit a key result on their own, and a glob that resolves only to ignored files raises `scope-unobservable`.
- Detector coverage is narrow by design. Syntax checking covers `.js`, `.mjs`, and `.cjs` only; TypeScript, JSX, and Java files are measured as surface but not syntax-checked.
- Telemetry proves usage, not benefit. `oguard inventory dashboard` explicitly declares `causal_effect: not-evaluable`: usage volume and a later correction do not attribute avoided drift, saved time, or saved tokens to the guard.
- The pre-edit gate is fail-open. Repositories without a typed contract are not blocked, and the gate does not attempt to classify arbitrary writes inside shell commands. That partial coverage is stated rather than presented as isolation.

## Working inside an agent harness

Inside a coding-agent harness the flow above is enforced rather than remembered. The harness prompt injects `oguard task-flow` at the start of each request, and a global `PreToolUse` hook over the structured `Edit` and `Write` tools runs the local gate. The gate allows the edit only when the most recent `task-flow: ready` matches the current contract hash and KPI policy hash and is less than one hour old; otherwise the tool call receives `permissionDecision: deny` with the command to run. Editing through shell commands is outside its coverage. The `OGUARD_HARNESS` environment variable labels which harness produced each telemetry event.

Per-harness setup lives in the harness guides:

- [Harness guides overview](guides/README.md)
- [Claude Code](guides/claude-code.md)
- [Codex](guides/codex.md)

## Command reference

`oguard --help` prints the full usage. Every command below is verified against `src/cli.mjs`.

### Setup and intent

| Command | What it does | When you need it |
| --- | --- | --- |
| `init` | Creates `oguard.contract.json` in `observe` mode with a placeholder objective; refuses to overwrite. | Once per repository, before anything else. |
| `intent --objective-id --objective --task-type --scope` | Saves a revisable local draft intent in `.oguard/intent.json`. | Before freezing a contract, while the objective or scope is still a hypothesis. |
| `intent-scan` | Resolves the draft globs against Git-observable files and lists test candidates. | To confirm the candidate scope resolves to the files you meant. |
| `intent-status` | Reports whether a local draft exists and whether the contract is missing, placeholder, or typed. | To review the draft before promoting it. |
| `start … [--from-intent]` | Fills the placeholder into one typed round and freezes `.oguard/session-baseline.json`. | Once per repository; afterwards use `next-round`. |
| `plan-template` | Prints the minimal `oguard.plan.json` derived from the typed contract. | When you want a versioned plan outside the round contract. |
| `anchor-plan` | Anchors a committed `oguard.plan.json` into the active baseline. | After committing the plan, from a clean tree. |

### Round lifecycle

| Command | What it does | When you need it |
| --- | --- | --- |
| `task-flow [--json]` | Verifies typed contract, baseline match, governance health, and KPI policy; grants a one-hour preflight or exits non-zero. | Before the first material edit of every request. |
| `next-round …` | Opens a typed successor round, archiving the previous contract and baseline under `.oguard/history/`. | Before new work, with a clean tree and a matching baseline. |
| `adopt-baseline` | Creates the first baseline for a historical typed contract that has none. | One-time migration only; normal transitions use `next-round`. |
| `register-plan-amendment` | Archives the current plan content and logs the amendment before you edit the plan. | Before changing an anchored `oguard.plan.json`. |

### Verification

| Command | What it does | When you need it |
| --- | --- | --- |
| `check --file <path>` | Runs the four universal detectors over the added lines of one file; exits 1 on an `error` finding. | After each material edit. |
| `align` | Reports objective, round, change surface, per-key-result evidence, and alignment warnings; always exits 0. | After a patch, to see drift before closing. |
| `round-end [--mode fast\|final]` | Runs detectors over changed files, the mode's declared checks, `git diff --check`, criteria, and obligations; exits 1 on `block`. | To close a round; `final` decides promotability. |

### Findings, adjudication, and governance

| Command | What it does | When you need it |
| --- | --- | --- |
| `list` | Prints the active findings held in `.oguard/state.json`. | To see the full detail behind a truncated `check` output. |
| `label <finding-id> --outcome` | Labels a finding `true-positive`, `false-positive`, or `unclear`. | When building calibration data from real findings. |
| `outcome --outcome --note` | Records a human verdict for the last closed round without altering it. | After reviewing a closed round. |
| `health` | Consolidates plan hash, amendments, integrity, and active/resolved findings. Implemented but absent from `--help`. | To inspect governance health on its own. |
| `reset` | Discards local finding state. | Only after reviewing `.oguard/state.json`. |

### Evidence

| Command | What it does | When you need it |
| --- | --- | --- |
| `evidence` | Prints a summary of rounds, classifications, labels, and unlabeled findings. | For a quick local status. |
| `evidence export --output evidence/runs/<name>.json` | Writes the sanitized evidence artifact for the recorded rounds. | At the end of a round. |
| `evidence report --output evidence/runs/<name>.json` | Writes the passive report, including recorded outcomes. | For review or handoff. |
| `evidence dataset calibration\|evaluation --output evidence/runs/<name>.json` | Writes a passive, split dataset with versioned features. | For offline calibration work; it triggers no thresholds. |

### Telemetry and KPIs

| Command | What it does | When you need it |
| --- | --- | --- |
| `inventory discover [--root <path>]` | Lists Git projects under the given root with their last guard event. | To see where the guard is installed. Pass `--root`: the defaults are absolute paths from the author's machine. |
| `inventory status` | Reports the global inventory state. | To confirm telemetry is being written. |
| `inventory report` | Aggregates sanitized global events, including `pre_edit` totals. | To review adoption without reading raw events. |
| `inventory kpis [--policy <file>]` | Derives preflight coverage, denial rate, p95 latency, and finding resolution; validates thresholds and exits 1 on a blocking violation. | To enforce a KPI policy locally or in CI. |
| `inventory dashboard [--days N]` | Aggregates adoption, flow discipline, intervention, and resolution by project and harness. | For a usage overview; it declares `causal_effect: not-evaluable`. |

### Exceptions and recovery

| Command | What it does | When you need it |
| --- | --- | --- |
| `reanchor-baseline --reason <text>` | Moves the baseline to a contract changed after a passing round. | Only after a `round-end` with `pass` for the current contract. |
| `accept-baseline --reason <text> --actor <text>` | Records an explicit local acceptance of non-promotable debt, preserving the failed or unknown status. | When an owner accepts a closed round's result to unblock later work. |

Auditable exceptions to policy warnings are declared in the contract's `exceptions[]` array (`id`, `rule`, `reason`, `actor`, a future `expires_at`, non-empty `scope.paths`), not through a CLI command. `round-end` keeps the original warning and logs `exception-applied` separately.

## Where to go next

- [Documentation index](README.md)
- [Vision and thesis](product/01-vision-and-thesis.md)
- [Product contract](product/02-product-contract.md)
- [Functional map](functionality/INDEX.md)
- [Episodes and findings](episodes/README.md)
- [Harness guides](guides/README.md)
