Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions .agents/skills/adding-inbox-sources/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ source-list-relevant is a place you must add the new product. The canonical list

### Type gates (every source)

1. `packages/shared/src/inbox-types.ts` — add `"jira"` to the `SourceProduct` union.
1. `packages/agent/packages/agent-contracts/src/inbox-types.ts` — add `"jira"` to the `SourceProduct` union.
2. `packages/api-client/src/posthog-client.ts` — add to `SignalSourceConfig.source_product` union; add a new `source_type` value only if the record type isn't already `issue`/`ticket`.

### Live UI path (every source)
Expand Down Expand Up @@ -145,7 +145,7 @@ service, symbol, or router — do not clone `linear.ts`/`linear-integration.rout

### Verify

- `pnpm --filter @posthog/shared build` after touching `inbox-types.ts` (it's a published type).
- `pnpm --filter @posthog/shared... build` after touching `inbox-types.ts` (it's a published type).
- `pnpm typecheck` (whole repo — the unions are consumed across packages).
- `biome lint packages/core packages/ui` — zero `noRestrictedImports`, imports ordered.

Expand Down
4 changes: 4 additions & 0 deletions .agents/skills/posthog-desktop/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ Step outside only when one of these is true:
`.dockerignore`, `.oxlintrc.json`, `.oxfmtrc.json`, `.config/.markdownlint-cli2.jsonc`,
`.github/workflows/ci-{frontend,storybook,backend}.yml` (+ `.depot/workflows/ci-backend.yml`).
Touch these only to keep an exclusion correct; say so when you do.
4. **The agent runtime changes.** `@posthog/agent`, `agent-contracts`, `enricher`, `git` and
`harness` live in a sibling workspace at `packages/agent/` (same toolchain, own lockfile),
because cloud sandboxes boot the agent without the desktop app. Work there, then run
`pnpm build:agent` from `products/desktop/` so desktop's turbo cache sees the change.

Anything else outside the tree: stop and ask.

Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/running-ci-preflight/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ hogli ci:preflight --fix
- **`type-check` is a nudge, not a run.** Only a repo-wide mypy run matches CI (mypy follows imports, so a changed-file subset misses reverse-dependency breakage), and that costs minutes cold — too much to tax every push with. So preflight names the command instead of running it: judge whether your change is type-risky and run it yourself. CI blocks on the same command, so a type error you skip here comes back as a full re-run.
- **`complexity` warns only.** Cyclomatic complexity above 10 in a changed file shows as `⚠ warning` and never blocks; CI annotates the same warning and posts it to the CI report comment. Simplify the function when you next touch it rather than gaming the number.
- **Staleness is risk-based.** It fires when merging master _now_ would actually break something — textual merge conflicts (computed via `git merge-tree`, working tree untouched), migrations added on both sides, generated-file inputs changed on both sides, or CI workflows changed on master — plus a behind/age backstop, aggressive by default (5 commits / 2 days; env-tunable via `HOGLI_PREFLIGHT_STALE_COMMITS`/`HOGLI_PREFLIGHT_STALE_DAYS`) so we over-warn to start and tune down from telemetry. Merge master in when it fires. Advisory only, never auto-merged.
- **`· skipped (needs …)`** is expected on a bare checkout or sandbox. `needs stack`/`needs clickhouse` want a running dev stack (`hogli start`), `needs node` wants `pnpm install`, `needs desktop-node` wants `pnpm --dir products/desktop install` (the nested desktop workspace is excluded from the root install and has its own lockfile), and `needs python-env` wants `uv sync`, so the synced project environment is the `python` on PATH. Satisfy what you can, or let CI cover the rest. No hooks in your environment (no `node_modules`)? Run the loop yourself before pushing.
- **`· skipped (needs …)`** is expected on a bare checkout or sandbox. `needs stack`/`needs clickhouse` want a running dev stack (`hogli start`), `needs node` wants `pnpm install`, `needs desktop-node` wants `pnpm --dir products/desktop install` and `needs agent-node` wants `pnpm --dir packages/agent install` (both nested workspaces are excluded from the root install and have their own lockfiles), and `needs python-env` wants `uv sync`, so the synced project environment is the `python` on PATH. Satisfy what you can, or let CI cover the rest. No hooks in your environment (no `node_modules`)? Run the loop yourself before pushing.
- **Flags.** `--against <ref>` diffs against an explicit base; `--json` emits a machine-readable summary.
- **Kill switch.** `HOGLI_PREFLIGHT_DISABLED=1` makes the command (and the hook) a no-op with exit 0. It is a rollout/emergency lever — respect it; never unset it to force a run.

Expand Down
4 changes: 2 additions & 2 deletions .claude/rules/task-model-catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ paths:
- 'products/tasks/backend/model_catalog.py'
- 'products/tasks/scripts/model_catalog_projection.py'
- 'products/tasks/frontend/modelCatalog.generated.ts'
- 'products/desktop/packages/shared/src/model-catalog.generated.ts'
- 'packages/agent/packages/agent-contracts/src/model-catalog.generated.ts'
---

`products/tasks/backend/model_catalog.py` is the single definition of a task run's triple: runtime adapter, model, and reasoning effort.
Expand All @@ -12,7 +12,7 @@ The web composer and the desktop app each read a checked-in TypeScript projectio
After changing the catalog, run `hogli build:projections` and commit both regenerated files.
The command needs no dev stack and takes well under a second, so run it rather than reasoning about whether the output moved.

Never hand-edit `products/tasks/frontend/modelCatalog.generated.ts` or `products/desktop/packages/shared/src/model-catalog.generated.ts`.
Never hand-edit `products/tasks/frontend/modelCatalog.generated.ts` or `packages/agent/packages/agent-contracts/src/model-catalog.generated.ts`.
`hogli build:projections --check` re-renders both and compares them byte for byte in CI, so an edit that the renderer would not produce fails CI rather than shipping.

Adding a model means one row in `MODELS`. Two things do not follow from that row and are worth checking:
Expand Down
2 changes: 1 addition & 1 deletion .config/.markdownlint-cli2.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
"MD045": false,
"MD029": false,
},
"ignores": ["products/desktop/**", "**/fixtures/**"],
"ignores": ["products/desktop/**", "packages/agent/**", "**/fixtures/**"],
}
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ OIDC_RSA_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFA
# RS256 private key for sandbox JWT authentication (public key is derived from this)
SANDBOX_JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDMAaMESiOX8PDM\nozOHunt1s+zp78nmLnHs6IvOoKS4rQXFVV2/ouTivIfLRMRDe2TAybsYK0yfAJeK\nFxW7O+MD/vjCMS7kJtOjskQIl3wQTjz/T4fwHkFpiqVHLWczeO5LdvaJBZ4FUYdc\nLaV+9T+4wEavJa5G/Ggl+9eWee/TuThN21wMuAQb7WZhWKqDVm8WfmZpvOXJJ/U1\nkg+8GVg7eMTs6GV0MI7YYwzEHRn3PUQrQmc+q6gROwHU7pEv8DdOLjDH5iYZL/4k\ndnW1KGO/Hqf7vzKEMN23YjL6bC4FDRfM5z/4nQUEjvXihzZ1yK6yChwEd7Ma/PUG\nIh6Pm5azAgMBAAECggEAE/nlDeUkmcWMuWe8WAIOrMvdhbARQ7+F2v46Z7/wi+ow\nB86qy2UWpzfGzu9WOIq7UZVvWJfaJ4erTxOg8SCVbi1oC7vn14y7FJW+y7M/AWm9\nLKg2VhuK/twGAHOVs23tXOkH4wrwb5ziyvKSo109zB19I7wQ1f/z7XmSTA3Mn3Kf\n6GecR6di+LuDUNeEPwuP061EnUUsSYZY/QnhUNHfAyWaO4Ezc4bdl8w2q3O1ElBo\ndaYx9ht9aw8bTjIAS6QUdnh3OPXrQ/E7+2RaEh6NFFuXsh93aGSXKB6ZgURk0wIc\nBtNGA5Sxur8nwMfYT+6Z8fi7wMGuGtKISNK+DXCVwQKBgQD+nG92xjZebauJaYOn\nK7cDgWza9zwG6nDv13vRPS+D9wlJP3eUH1O+vZsar9UdTrXGh51oBskc4giOK9aL\nOvp9SjfrRx9KbkLLTzcS+r0XIYog0TO0onswaJ+ybwuvXU6zx+GjoKmPFDYrsjC5\n/3AkBPgEhorM4bZ1plfsFMzbaQKBgQDNHogqPPIXSco9ESTA6b3IeyzEGVcODDFq\nZgEIuFZJ70FCgg+TfxvZyQKfEGjxg6nLKQswaAQ7Pt3yMvYmEiVTrxaLBIjG9Lu8\nTWOMmJDiA7rgFVsU5ENI6UH8Nv8FkS5JSAPgC8hFnLKvs/9RjCTET3UCGoK5ymdM\nO1c/L0epuwKBgDYLUKGeizXaA5pEWlymq89Drq5/4i75noVacP7GBQr26fKxVRmM\n2MLZDk5I2mzBI1aDvMazAgdudzBuX7joCPmFQn9fdmXfJ7BuHRubO33ocaBrp5UF\nFC7/Vj6S0aEkpisFF4Ea/kLPoHv/89XXQZ9zqo2TXW0F0CwC5hDHjYBZAoGAG4IN\ntsRnnxHpSllDOY/fQMSsEzxmvV6LPf6iAt5dzBqHAPlaR2iTfpiDfnt/52vF2JYP\nhxVcA+oBb9q/wglK8jcX6drY0P2/M5iZUiCfxX/EjwquJVYbY+rMS5vAhambsH40\n7tYFrLhACmo0QmZuA9m4EmklwO7Q/ZszryiTDsMCgYBUUMWlQfuTN8m+D/Eo614J\neRYx8/YcaQpASOzy74woAZyHGLjuu5eW9wclkdhklHGh6KyQ5B6JUXlQsbZxLbBb\n9oW6Jg+UvL0KKLqHoNmNitiAUGONrVpxLjrKUUrUfDD+sWsxQpzcOgBqalTZ2g30\nN6ctjTLJp5PqIIE9tZB9PQ==\n-----END PRIVATE KEY-----"

# Cloud tasks use the published @posthog/agent package by default. Uncomment this only when testing local agent changes. The desktop source lives in this repo at products/desktop (the standalone PostHog/code repo is archived).
# LOCAL_POSTHOG_CODE_MONOREPO_ROOT=./products/desktop
# Cloud tasks use the published @posthog/agent package by default. Uncomment this only when testing local agent changes. The agent workspace lives in this repo at packages/agent (the standalone PostHog/code repo is archived).
# LOCAL_POSTHOG_CODE_MONOREPO_ROOT=./packages/agent

# Sandbox provider (you can choose between "docker" or "modal")
SANDBOX_PROVIDER=docker
Expand Down
4 changes: 2 additions & 2 deletions .github/actions/desktop-build-agent-release/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ runs:
steps:
- name: Build agent and workspace dependencies
shell: bash
working-directory: products/desktop
working-directory: packages/agent
run: pnpm --filter @posthog/agent... run build

- name: Run agent tests
shell: bash
working-directory: products/desktop
working-directory: packages/agent
run: pnpm --filter @posthog/agent run test
2 changes: 2 additions & 0 deletions .github/scripts/desktop/check-pr-backend-coupling.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ is_backend_path() {
is_desktop_path() {
case "$1" in
products/desktop/*) return 0 ;;
# The desktop app bundles these packages, so they ship on its release schedule.
packages/agent/packages/*) return 0 ;;
*) return 1 ;;
esac
}
Expand Down
6 changes: 6 additions & 0 deletions .github/scripts/desktop/check-pr-backend-coupling.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ assert_result "backend-only PR is out of scope" 2 0 "No products/desktop changes
register_pr 3 "[]" products/desktop/apps/foo.ts posthog/models.py
assert_result "coupled PR fails with split guidance" 3 1 "must be separated into different PRs"

register_pr 7 "[]" packages/agent/packages/agent/src/agent.ts posthog/models.py
assert_result "agent workspace package coupled with backend fails" 7 1 "must be separated into different PRs"

register_pr 8 "[]" packages/agent/agent-shadow/main.go products/tasks/backend/sandbox/images/Dockerfile.sandbox-base
assert_result "agent-shadow ships with the sandbox image, so it is out of scope" 8 0 "No products/desktop changes"

register_pr 4 '[{"name": "desktop-skip-backend-check"}]' products/desktop/apps/foo.ts posthog/models.py
assert_result "skip label suppresses the check" 4 0 "skipping the coupling check"

Expand Down
6 changes: 6 additions & 0 deletions .github/scripts/trunk-impacted-targets.js
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,7 @@ const COMMON_FULLSTACK = ['fixtures']

// The pr-approval-agent engine's home inside the stamphog product.
const PR_APPROVAL_AGENT_DIR = 'products/stamphog/packages/pr-approval-agent'
const AGENT_WORKSPACE_DIR = 'packages/agent'

// Tools that own their whole test story and that no suite imports, so they can
// hold a lane of their own. Everything else under tools/ falls through to the
Expand Down Expand Up @@ -1973,6 +1974,11 @@ function computeTargets(changedFiles, context) {
allPyProducts()
continue
}
// The desktop app bundles the agent workspace and the desktop-* workflows test it,
// so it shares the desktop lanes. Without the product it takes the frontend lanes.
if (file.startsWith(`${AGENT_WORKSPACE_DIR}/`) && addDesktopLanes(targets, context)) {
continue
}
if (top === 'frontend' || (top === 'ee' && segments[1] === 'frontend') || top === 'packages') {
allFeProducts()
continue
Expand Down
17 changes: 15 additions & 2 deletions .github/scripts/trunk-impacted-targets.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,10 @@ test('the paths-filter action and its CI share the ci-tooling lane', () => {
// and depot.json is billing and cache routing that fails its own PR's builds
// alone.
test('pnpm patches take the JS lanes and depot.json the repo-config lane', () => {
assert.deepEqual(computeTargets(['patches/dayjs@1.11.11.patch'], CONTEXT), computeTargets(['.oxlintrc.json'], CONTEXT))
assert.deepEqual(
computeTargets(['patches/dayjs@1.11.11.patch'], CONTEXT),
computeTargets(['.oxlintrc.json'], CONTEXT)
)
assert.deepEqual(computeTargets(['depot.json'], CONTEXT), ['repo-config'])
})

Expand Down Expand Up @@ -769,6 +772,13 @@ test('desktop workflows claim the desktop product lanes and widen without the pr
assert.deepEqual(computeTargets(['.github/workflows/desktop-ci.yml'], CONTEXT), EVERYTHING)
})

test('the agent workspace shares the desktop lanes and takes the frontend lanes without the product', () => {
const withDesktop = { ...CONTEXT, products: [...CONTEXT.products, 'desktop'] }
const agentFile = 'packages/agent/packages/agent/src/index.ts'
assert.deepEqual(computeTargets([agentFile], withDesktop), ['fe:product:desktop', 'py:product:desktop'])
assert.deepEqual(computeTargets([agentFile], CONTEXT), computeTargets(['packages/quill/src/index.ts'], CONTEXT))
})

// The Proto CI workflow gates buf lint and the stub drift checks over every
// tree, which is the same radius the root buf configuration gets.
test('the proto workflow claims every proto tree rather than everything', () => {
Expand Down Expand Up @@ -810,7 +820,10 @@ test('the agent-skills workflow claims both language families', () => {
})

test('the ml-mirror sidecar image and its workflow stay on the node lane', () => {
for (const file of ['.github/workflows/ci-ml-mirror-image-scrub-container.yml', 'Dockerfile.ml-mirror-image-scrub']) {
for (const file of [
'.github/workflows/ci-ml-mirror-image-scrub-container.yml',
'Dockerfile.ml-mirror-image-scrub',
]) {
assert.deepEqual(computeTargets([file], CONTEXT), ['node:ingestion'], file)
}
})
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,10 @@ jobs:
- 'frontend/src/taxonomy/core-filter-definitions-by-group.json'
- 'services/mcp/src/lib/trace-property-allowlist.generated.ts'
- 'products/desktop/packages/core/src/inbox/objectKinds.generated.ts'
- 'products/desktop/packages/shared/src/objectTagKinds.generated.ts'
- 'packages/agent/packages/agent-contracts/src/objectTagKinds.generated.ts'
- 'frontend/src/lib/components/AgentObjectTags/objectKinds.generated.ts'
- 'products/tasks/frontend/modelCatalog.generated.ts'
- 'products/desktop/packages/shared/src/model-catalog.generated.ts'
- 'packages/agent/packages/agent-contracts/src/model-catalog.generated.ts'
- 'services/mcp/src/lib/oauth-scopes.generated.ts'
# hogli CLI changes need validation
- 'tools/hogli/**'
Expand Down
12 changes: 3 additions & 9 deletions .github/workflows/desktop-agent-release-verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,10 @@ on:
branches:
- master
paths:
- 'products/desktop/packages/agent/**'
- 'products/desktop/packages/enricher/**'
- 'products/desktop/packages/git/**'
- 'products/desktop/packages/harness/**'
- 'products/desktop/packages/shared/**'
- 'packages/agent/**'
- '.github/actions/desktop-build-agent-release/**'
- '.github/workflows/desktop-agent-release.yml'
- '.github/workflows/desktop-agent-release-verify.yml'
- 'products/desktop/pnpm-lock.yaml'
- 'products/desktop/pnpm-workspace.yaml'

env:
# npm's audit blocks the pnpm bootstrap. See .github/actions/pnpm-install for why.
Expand All @@ -34,7 +28,7 @@ jobs:
if: ${{ !startsWith(github.head_ref, 'trunk-merge/') }}
defaults:
run:
working-directory: products/desktop
working-directory: packages/agent
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand All @@ -44,7 +38,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
with:
package_json_file: products/desktop/package.json
package_json_file: packages/agent/package.json

- name: Set up Node 24
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/desktop-agent-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
tarball_sha256: ${{ steps.pack.outputs.tarball_sha256 }}
defaults:
run:
working-directory: products/desktop
working-directory: packages/agent
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand All @@ -40,7 +40,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
with:
package_json_file: products/desktop/package.json
package_json_file: packages/agent/package.json

- name: Set up Node 24
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
Expand Down Expand Up @@ -70,7 +70,7 @@ jobs:

- name: Pack the release tarball
id: pack
working-directory: products/desktop/packages/agent
working-directory: packages/agent/packages/agent
env:
AGENT_VERSION: ${{ steps.version.outputs.version }}
run: |
Expand All @@ -88,7 +88,7 @@ jobs:
retention-days: 1

- name: Publish the package to npm registry
working-directory: products/desktop/packages/agent
working-directory: packages/agent/packages/agent
run: pnpm publish --access public --no-git-checks
env:
NPM_CONFIG_PROVENANCE: true
Expand Down
11 changes: 7 additions & 4 deletions .github/workflows/desktop-agent-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ on:
branches:
- master
paths:
- 'products/desktop/packages/agent/**'
- 'products/desktop/packages/harness/**'
- 'packages/agent/packages/agent/**'
- 'packages/agent/packages/harness/**'
- '.github/workflows/desktop-agent-tag.yml'
- '.github/workflows/desktop-agent-release.yml'
workflow_dispatch:
Expand Down Expand Up @@ -87,10 +87,13 @@ jobs:

# Count commits touching the agent or harness package since the base tag. In the
# monorepo an unscoped count would include every PostHog commit and
# inflate patch numbers meaninglessly.
# inflate patch numbers meaninglessly. The old paths stay in the pathspec so
# the count does not restart below versions that are already published.
PATCH=$(git rev-list "$LATEST_TAG".."$TARGET" --count -- \
products/desktop/packages/agent \
products/desktop/packages/harness)
products/desktop/packages/harness \
packages/agent/packages/agent \
packages/agent/packages/harness)

if [ "$PATCH" -eq 0 ]; then
echo "No agent or harness commits since $LATEST_TAG. Nothing to release."
Expand Down
Loading
Loading