Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
4aca7c5
feat(0.2): AI detector batch + calibration regression gate + RAG plum…
pmclSF May 1, 2026
fa35586
feat(0.2): Eval-data calibration fixtures (cost / hallucination / ret…
pmclSF May 1, 2026
af34768
feat(0.2): CLI restructure phase A — 35→11 commands via namespaces
pmclSF May 1, 2026
9010c6b
fix(0.2): Ship-blocker fixes from adversarial review + honest CHANGELOG
pmclSF May 1, 2026
80d2c10
fix(0.2): Ship-blocker fixes from adversarial review
pmclSF May 1, 2026
193a782
fix(0.2): Final polish — release blockers, detector-panic catalog, do…
pmclSF May 2, 2026
60b9838
docs(0.2): Public-facing documentation refresh for 0.2.0 tag (#129)
pmclSF May 2, 2026
3cab666
fix(0.2): CLI visual polish — pluralization, dimension labels, measur…
pmclSF May 2, 2026
8545f03
fix(0.2): truth-up the public surface before tag (#131)
pmclSF May 2, 2026
c4c19a9
feat(0.2): parity rubric + baseline scores (Track 0.1) (#135)
pmclSF May 4, 2026
1713a87
feat(0.2): design tokens package — internal/uitokens/ (Track 10.1) (#…
pmclSF May 4, 2026
963d8c9
docs(0.2): product vision + README/quickstart/feature-status/CHANGELO…
pmclSF May 4, 2026
e0619da
fix(0.2.1): surface install failures via marker file (Option B) (#133)
pmclSF May 4, 2026
d0132a3
feat(0.2.1): --fail-on and --timeout flags on terrain analyze (#134)
pmclSF May 4, 2026
00c166b
feat(0.2): stable finding IDs (Track 4.4) (#138)
pmclSF May 4, 2026
7707536
feat(0.2): hallucination-rate framing + policy templates (Tracks 5.2/…
pmclSF May 4, 2026
75516ab
feat(0.2): CI onboarding — GitHub Action template + trust ladder + in…
pmclSF May 4, 2026
569aa58
feat(0.2): finish Track 3 centerpiece — integration / e2e / unified P…
pmclSF May 4, 2026
a2759c1
feat(0.2): AI risk subdivision + ctx audit on AI detectors (#146)
pmclSF May 4, 2026
395440f
feat(0.2): adapter conformance + Promptfoo CI walkthrough (Track 7) (…
pmclSF May 4, 2026
ce2c594
feat(0.2): empty-state helpers + visual regression goldens (Tracks 10…
pmclSF May 4, 2026
055fe83
test(0.2): adversarial filesystem suite + 0.1.x schema migration fixt…
pmclSF May 4, 2026
47b816f
docs(0.2): multi-repo alignment example fixture (Track 6.4) (#152)
pmclSF May 4, 2026
090cb7f
test(0.2): memory benchmark suite + ceiling tests (Track 9.10) (#153)
pmclSF May 4, 2026
352201d
feat(0.2): per-detector wall-clock timeout budgets (Track 9.4) (#154)
pmclSF May 4, 2026
c47155d
docs(0.2): AI trust boundary + Node 22 prominence + release-smoke mat…
pmclSF May 4, 2026
5380139
feat(0.2): report pr --fail-on + report impact --explain-selection (T…
pmclSF May 4, 2026
93d1dd6
feat(0.2): docs-linkcheck binary + schema field tiers (Tracks 9.8/9.1…
pmclSF May 4, 2026
bc34641
chore: ignore .claude/ and stray cmd binaries (#159)
pmclSF May 4, 2026
dc01edc
fix(0.2.1): wire request context + dedup in-flight server analyses (#…
pmclSF May 4, 2026
6340df3
feat(0.2): Align pillar foundation — repos.yaml + alignment-first mig…
pmclSF May 4, 2026
d9ddc08
feat(0.2): detector capability metadata + missing-input diagnostics (…
pmclSF May 4, 2026
2ce1fbd
feat(0.2): truth-verify gate — feature-status doc ⊆ signal manifest (…
pmclSF May 4, 2026
6ba19d0
feat(0.2): suppression model — .terrain/suppressions.yaml (Track 4.5)…
pmclSF May 4, 2026
ee2a628
chore: parity scores + linkcheck fixes after merge wave (#160)
pmclSF May 5, 2026
8190829
feat(0.2): renderer migration to uitokens — bracketed severity/verdic…
pmclSF May 5, 2026
475dc32
feat(0.2): unified progress indicators (Track 10.5) (#162)
pmclSF May 5, 2026
380b000
feat(0.2): voice-and-tone lint (Track 10.7) (#163)
pmclSF May 5, 2026
f1b8482
feat(0.2): wire progress.Spinner into runConvert (Track 10.5 follow-o…
pmclSF May 5, 2026
128cb5f
feat(0.2): command registry foundation (Track 9.6) (#165)
pmclSF May 5, 2026
72827b1
fix(0.2): adopter-facing polish caught running the binary cold (#166)
pmclSF May 5, 2026
4e65439
feat(0.2): Gate pillar lift — hero verdict + adapter diagnostics + po…
pmclSF May 7, 2026
cc15f23
fix(0.2): PR #140 recovery + Track 2 pillar markers + V3 empty-states…
pmclSF May 7, 2026
e1d910e
feat(0.2): Gate pillar lift batch 2 — final lifts before 0.3 corpus w…
pmclSF May 7, 2026
b979a93
feat(0.2): pillar lift batch 3 — portfolio + explain schemas + benchm…
pmclSF May 7, 2026
631379b
feat(0.2): final pillar lifts — Understand pillar PASSES at floor=3 (…
pmclSF May 7, 2026
15d6bb6
chore(deps-dev): bump @commitlint/config-conventional
dependabot[bot] May 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
54 changes: 54 additions & 0 deletions .github/ISSUE_TEMPLATE/bug-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
name: Bug report
about: Something doesn't work the way you expect
title: '[bug] '
labels: bug
assignees: ''
---

## Summary

What happened, in one sentence.

## Reproduction

Minimal steps to reproduce (or a public repo + commit SHA we can clone):

```bash
# 1.
# 2.
# 3.
```

## Expected vs actual

- **Expected:**
- **Actual:**

## Environment

- `terrain version --json` output:
```
paste here
```
- OS / arch (e.g. macOS 14.5 arm64):
- Install method (npm / homebrew / go install / pre-built / source):
- Terrain config in `.terrain/` (if any):

## Logs

If the bug surfaces an error, paste the full stderr + the output of
re-running with `--log-level=debug`:

<details>
<summary>Debug output</summary>

```
paste here
```

</details>

## Anything else

Screenshots, profiler output, hypotheses, related issues, etc.
53 changes: 53 additions & 0 deletions .github/ISSUE_TEMPLATE/false-positive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
name: False positive
about: A detector fired but the underlying code is fine
title: '[fp] '
labels: false-positive, calibration
assignees: ''
---

Detector false positives directly affect calibration (precision). The
more concrete the reproduction, the easier it is to add a labeled
fixture under `tests/calibration/` so the regression doesn't come back.

## Detector

Type the exact signal type as it appears in `terrain analyze --json`
(e.g. `aiToolWithoutSandbox`, `weakAssertion`).

## Code that triggered the finding

The minimal source / config snippet that caused the detector to fire,
with surrounding context preserved:

```yaml
# or .py / .ts / .go etc.
paste here
```

## Why this isn't actually a problem

In one or two sentences, why this code is fine despite matching the
detector's pattern. Example: "`delete_cache` is a request-scoped LRU
clear, not a destructive data operation."

## Detector output

The full signal as it appears in `--json` output:

```json
paste here
```

## Suggested fix shape

If you have a sense for what would close this — a noun whitelist
expansion, a confidence downgrade, a path-shape exclusion — name it.
The maintainers will translate the suggestion into a concrete
detector change.

## Calibration corpus opt-in

If you can share the snippet under an open-source license, would you
be willing to have it added to `tests/calibration/<detector>/` with
`expectedAbsent: <signal>` so this regression is locked out? Yes/no.
33 changes: 33 additions & 0 deletions .github/ISSUE_TEMPLATE/feature-request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
name: Feature request
about: A capability you'd like Terrain to have
title: '[feat] '
labels: enhancement
assignees: ''
---

## What you want to do

The user-facing problem, framed as a job-to-be-done. Avoid jumping to
implementation; lead with the outcome.

## Why today's Terrain doesn't get you there

Which specific commands / signals / outputs you've tried, and where
they fall short. If a workaround exists, describe it briefly.

## What "done" looks like

Concrete success criteria. Could be a CLI shape, a JSON field, a
report section, a CI gate. The more specific, the easier to scope.

## Connections to existing work

Related issues / PRs / feature-status entries / known-gaps items.
Linking saves the maintainers a search.

## Optional: implementation hint

If you know the codebase, where you think the change goes (e.g. "new
detector under `internal/aidetect/`", "new flag on `terrain analyze`,
parsed in `cmd/terrain/cmd_analyze.go`"). Skip if you don't.
10 changes: 5 additions & 5 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ but specific.

<!-- Check all that apply. -->

- [ ] Bug fix (no behaviour change beyond the fix)
- [ ] New feature (additive only — no changes to existing behaviour)
- [ ] Breaking change (alters existing behaviour, schema, CLI, or output shape)
- [ ] Bug fix (no behavior change beyond the fix)
- [ ] New feature (additive only — no changes to existing behavior)
- [ ] Breaking change (alters existing behavior, schema, CLI, or output shape)
- [ ] Documentation only
- [ ] Test or tooling only
- [ ] Refactor (behaviour-preserving)
- [ ] Refactor (behavior-preserving)

## Reviewer checklist

Expand Down Expand Up @@ -61,5 +61,5 @@ note. If unsure, see docs/schema/COMPAT.md for the contract.
<!--
- [ ] Smoke-tested locally with: ...
- [ ] Added unit / integration tests for: ...
- [ ] Regression-tested existing behaviour for: ...
- [ ] Regression-tested existing behavior for: ...
-->
42 changes: 41 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,24 @@ on:
pull_request:
branches: [main]

# Cancel in-progress CI runs when a new commit is pushed to the same
# PR / branch. Pre-0.2.x final-polish, force-pushes piled up
# overlapping runs that all consumed runner minutes pointlessly.
# `cancel-in-progress: true` on PR-triggered runs keeps the queue
# clean; we leave main-branch runs uncancelled so post-merge runs
# always complete (they're rare and the result is load-bearing for
# release dashboards).
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
contents: read

jobs:
npm-package:
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Checkout repository
Expand Down Expand Up @@ -66,6 +78,7 @@ jobs:
- os: windows-latest
extended: false
runs-on: ${{ matrix.os }}
timeout-minutes: 30

steps:
- name: Checkout repository
Expand All @@ -84,6 +97,13 @@ jobs:
go mod tidy
git diff --exit-code go.mod go.sum

- name: Verify generated docs are up to date
if: matrix.extended
# Hard-fail gate (carries the 0.1.2 manifest scaffold to enforcement
# in 0.2). If you edit internal/signals/manifest.go, run
# `make docs-gen` and commit docs/signals/manifest.json.
run: make docs-verify

- name: Run core Go verification
if: matrix.extended
run: make go-release-verify
Expand Down Expand Up @@ -150,6 +170,7 @@ jobs:

extension:
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Checkout repository
Expand All @@ -169,6 +190,7 @@ jobs:
go-bench-compare:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Checkout repository
Expand Down Expand Up @@ -204,12 +226,30 @@ jobs:
git checkout "${{ github.sha }}"
"$(go env GOPATH)/bin/benchstat" /tmp/bench_base.txt /tmp/bench_head.txt | tee /tmp/benchstat.txt

- name: Append benchstat summary
- name: Regression gate (>10% fails the job)
# Carries the round-4 review's "perf regression suite establishes
# baseline" finding into a hard-fail gate. The gate parses the same
# benchmark output benchstat consumed, computes per-bench delta,
# and fails if any benchmark regressed beyond the threshold.
run: |
go run ./cmd/terrain-bench-gate \
--base /tmp/bench_base.txt \
--head /tmp/bench_head.txt \
--threshold 10 | tee /tmp/bench-gate.txt

- name: Append summary
if: always()
run: |
{
echo '### Go Benchmark Comparison (base vs head)'
echo ''
echo '```'
cat /tmp/benchstat.txt
echo '```'
echo ''
echo '### Regression gate (>10% threshold)'
echo ''
echo '```'
cat /tmp/bench-gate.txt 2>/dev/null || echo "(gate did not run)"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
12 changes: 11 additions & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,28 @@ on:
schedule:
- cron: '0 6 * * 1' # weekly Monday 6am UTC

concurrency:
group: codeql-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
security-events: write
contents: read

jobs:
analyze:
runs-on: ubuntu-latest
timeout-minutes: 30

strategy:
fail-fast: false
# 0.2.0 final-polish: dropped `python` from the language matrix.
# The repo has only a handful of Python helper scripts (no
# production Python under analysis); CodeQL Python autobuild was
# spending ~5 min/week with effectively no coverage. Re-add when
# the engine itself analyses Python source.
matrix:
language: [go, javascript-typescript, python]
language: [go, javascript-typescript]

steps:
- name: Checkout repository
Expand Down
90 changes: 90 additions & 0 deletions .github/workflows/homebrew-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: homebrew-update

# Updates the source-build formula in pmclSF/homebrew-terrain to point at
# the source tarball for a given tag. The existing formula is source-build
# (downloads refs/tags/<tag>.tar.gz and runs `go build`), so we just need
# the version and the tarball SHA256 — goreleaser is not involved.
#
# Triggers automatically when a v* tag is pushed AND when a release is
# published (so it runs after the main release workflow finishes), and
# also on manual workflow_dispatch as a recovery path.

on:
workflow_dispatch:
inputs:
tag:
description: 'Release tag (e.g. v0.1.2)'
required: true
type: string
release:
types: [published]

permissions:
contents: read

jobs:
update-formula:
runs-on: ubuntu-latest
steps:
- name: Resolve tag
id: tag
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${{ inputs.tag }}"
else
TAG="${{ github.event.release.tag_name }}"
fi
VERSION="${TAG#v}"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Updating brew formula for $TAG (version $VERSION)"

- name: Compute source tarball SHA256
id: sha
run: |
TAG="${{ steps.tag.outputs.tag }}"
URL="https://github.com/pmclSF/terrain/archive/refs/tags/${TAG}.tar.gz"
curl --fail --location --silent --show-error -o /tmp/source.tar.gz "$URL"
SHA=$(sha256sum /tmp/source.tar.gz | awk '{print $1}')
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
echo "SHA256: $SHA"

- name: Check out homebrew tap
uses: actions/checkout@v6
with:
repository: pmclSF/homebrew-terrain
token: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
path: tap

- name: Update Formula/mapterrain.rb
run: |
set -euo pipefail
TAG="${{ steps.tag.outputs.tag }}"
SHA="${{ steps.sha.outputs.sha }}"
FORMULA=tap/Formula/mapterrain.rb

# Replace the url line (matches refs/tags/<tag>.tar.gz suffix).
sed -i -E "s|refs/tags/v[0-9.]+\.tar\.gz|refs/tags/${TAG}.tar.gz|" "$FORMULA"
# Replace the sha256 line (the first one, since the formula has only
# one url/sha256 pair).
sed -i -E "0,/sha256 \"[a-f0-9]+\"/{s|sha256 \"[a-f0-9]+\"|sha256 \"${SHA}\"|}" "$FORMULA"

echo "=== Updated formula ==="
cat "$FORMULA"

- name: Commit + push formula update
working-directory: tap
run: |
set -euo pipefail
TAG="${{ steps.tag.outputs.tag }}"

if git diff --quiet; then
echo "Formula already up-to-date for $TAG; nothing to commit."
exit 0
fi

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add Formula/mapterrain.rb
git commit -m "mapterrain ${TAG}"
git push origin main
Loading
Loading