Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 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/build_model_catalog.py'
- 'products/tasks/frontend/modelCatalog.generated.ts'
- 'products/desktop/packages/agent-contracts/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:task-model-catalog` 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/agent-contracts/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`.
A test re-renders both and compares them byte for byte, so an edit that the generator 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/**"],
}
2 changes: 1 addition & 1 deletion .depot/workflows/ci-backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ jobs:
# filter excludes. The catalog and its generator are .py under
# products/, so the backend filter already catches both.
- 'products/tasks/frontend/modelCatalog.generated.ts'
- 'products/desktop/packages/agent-contracts/src/model-catalog.generated.ts'
- 'packages/agent/packages/agent-contracts/src/model-catalog.generated.ts'
- 'tools/openapi-codegen/**/*'
- 'services/mcp/scripts/lib/**'
- 'frontend/bin/generate-openapi-types.mjs'
Expand Down
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
2 changes: 1 addition & 1 deletion .github/workflows/ci-backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ jobs:
# filter excludes. The catalog and its generator are .py under
# products/, so the backend filter already catches both.
- 'products/tasks/frontend/modelCatalog.generated.ts'
- 'products/desktop/packages/agent-contracts/src/model-catalog.generated.ts'
- 'packages/agent/packages/agent-contracts/src/model-catalog.generated.ts'
- 'tools/openapi-codegen/**/*'
- 'services/mcp/scripts/lib/**'
- 'frontend/bin/generate-openapi-types.mjs'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ jobs:
# Same for the generated object-tag registries
- 'frontend/src/lib/components/AgentObjectTags/objectKinds.generated.ts'
- 'products/desktop/packages/core/src/inbox/objectKinds.generated.ts'
- 'products/desktop/packages/agent-contracts/src/objectTagKinds.generated.ts'
- 'packages/agent/packages/agent-contracts/src/objectTagKinds.generated.ts'
# hogli CLI changes need validation
- 'tools/hogli/**'
- 'tools/hogli-commands/hogli_commands/**'
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/agent-contracts/**'
- '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