release-2026-08-28
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
15c72ade1c |
Fix twenty findings from the Motion review
Each was raised by a reviewer and then survived an independent attempt to refute it. The four that mattered most: - A third of the starter library was invisible. Three templates authored `fields` shapes no renderer read — decisions, blockingSet, checks, steps and the rest — so about forty records rendered as no DOM at all, in the library and again on the engagement that instantiated them. Nothing failed: a renderer returns null for a key set it does not recognise, and a header-plus-body page looks like a template written that way. FieldsView now reads every key the seeds carry. - "Add a framework" opened a picker that could never match, because the dialog was seeded with both the forced kind and the deal's stage, and qualification serves only the qualification stage. The stage is now dropped when MOTION_KIND_STAGES says the pair is incoherent. - Piggy reported the promotion count as an exact figure capped at 8, against a tile showing the true count beside it. It is now counted in SQL, and all three motion tools carry a ResultScope whose denominator is shared lineages — never rows, never private drafts. - No Motion test went through createApp, so the whole feature could be unmounted with a green suite. That is the AGENTS.md §5 trap that already cost this project read-guards.ts and learn.ts. Also: both sides of the instantiate/edit race now lock, so a template cannot be rewritten under an artefact that has copied it; concurrent engagement opens queue on the deal row and get the 409 the handler already promised rather than a 500; latestScore uses DISTINCT ON instead of losing engagements past a 200-row cap; the migration adds the scored_by_user_id foreign key the schema declares; and the demo clear refunds usage_count for engagements it reaches by cascade, which otherwise left starter templates permanently un-editable. Verified on a fresh database: 16 migrations apply and re-apply as a no-op, both seeds idempotent, usage_count back to zero after --clear. 564 unit tests pass. Every Motion route measures zero horizontal overflow at 393 and 1440 in both themes, and all twelve seeded field trees are asserted onto the screen by scripts/motion-fields-check.mjs. One thing left open deliberately: the shipped qualification scorecard's five bands and MOTION_BANDS' four are calibrated differently. The framework's table is now titled as its own guidance rather than the product's verdict, which removes the contradiction on screen. Making the framework's calibration authoritative over the persisted band column is a product decision nobody has made. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f0173440e4 |
Put Piggy on Prime Agent, and let it write to the book
Piggy was a hand-rolled OpenAI tool loop. It is now a Prime Agent session — Prime Intellect's own harness, embedded as a Node library — answering from PIG's tools and, for the first time, able to put information into the CRM rather than only read it out. The harness is a coding agent, so the first job was taking the coding agent away from it. `noTools: 'all'` plus an explicit allowlist leaves the model with PIG's ten `pig_*` tools and no bash, no filesystem, no IPython. That holds under attack: a hostile extension, a skill and a settings file planted in the agent's own directory, then `setActiveToolsByName` called with every built-in, still leaves ten tools, all ours. Both lines are load-bearing — `noTools` alone registers nothing, and the allowlist is what admits our own. Writing is gated rather than assumed. A change is proposed, not made: the tool returns a description, the transcript renders a diff card, and nothing reaches the database until someone presses Apply. Contracts, commitments, allocations and compliance always stop for a human whatever the mode. Every write runs through `executeMutation` as the calling user, so their capabilities and the audit trail apply exactly as they would to a human's. Four things about the SDK are wrong in its own documentation and cost a debugging cycle each: models.json does not resolve an env var name for `apiKey`, it sends the literal string; there is no built-in prime-inference provider in 0.84.1; a ResourceLoader you pass in is never reloaded for you; and the stock system prompt is a coding-assistant prompt that must be replaced — but replacing it also silently removes the tool list, because the harness only renders that section when it owns the prompt. AGENTS.md records all four. The expensive one was thinking level. The harness defaults to `medium`, and nemotron spent an entire 4,096-token budget reasoning and returned an empty answer. `low` was worse; `off` omits the parameter so the endpoint's default wins. An explicit `reasoning_effort: none` via `thinkingLevelMap` took a turn from 6,195 output tokens to 149. And a turn is now bounded. The harness loop is `while (true)` with no iteration cap; a runaway on a frontier model would have eaten the credit it is supposed to report on. Ceilings on model calls and tokens, enforced both through the harness hook and independently from the event stream, plus a per-user daily spend limit — and the ledger now records spend on turns that fail, which it previously discarded. Signing in lands on /piggy, which is a workspace: conversations down one side, the agent in the middle, what it did and what it cost beside it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
13dec6b4b8 |
Rebuild the shell, add Calendar and Learn, and govern reads
Seven parallel agents and an adversarial verification pass. The three things worth knowing before reading the diff: RBAC WAS ALREADY BUILT. docs/build-plan.md marks F2 and F3 outstanding and is stale — packages/core/src/permissions.ts and lib/mutation.ts shipped long ago. So this does not rebuild them; it closes the gaps an audit found. The big one is that reads were entirely ungoverned: every GET was "any authenticated member", so a junior demand rep and a research contractor could both pull per-block supplier cost and break-even prices from /api/capacity/margin, and every contract's negotiated terms. For a company whose margin is the business, that was the hole that mattered. Adds book:read / economics:read / team:read, a readGuard middleware, and a `viewer` role below member. THE BUTTON AND THE 403 DISAGREED — the exact thing F3 said must never happen. Contracts.tsx never called can() at all, so its save button was always enabled against a server requiring contract:sign; Capacity.tsx gated commitment creation on deal:write/demand while the server wanted commitment:write/supply. POST /api/activities was the one write bypassing executeMutation: no capability check, and any member could mutate accounts.lastActivityAt as a side effect. It is now a proper mutation() behind activity:write. The shell becomes three panes — a collapsible shadcn sidebar with an account switcher on the Piggy accent, a header with real search, and Piggy docked to the right, page-aware and persistent across navigation. The phone keeps its bottom tab bar, which is the thing this product already beat trycompai/crm on, and gains the sidebar as a sheet. Calendar is a projection over thirteen dated sources rather than a new table, because a table would duplicate dates that already live on contracts, deals and commitments and would drift — and one ledger answering the question is the whole argument. It surfaces export_authorizations and compliance_artifacts, which had indexed expires_at columns, schema comments saying they must be alerted on, and no read endpoint or UI anywhere. Learn carries two tracks. Concepts are members-only; the platform track can be opened with a share code by someone with no account. The code mints a scoped learn-only token and never a Principal — every route here resolves a principal and then checks capabilities, so a principal-minting code would be one missing check away from leaking the book. "Only platform-track rows may be code-visible" is a database CHECK constraint as well as a write-path rule, and a test asserts a valid learn token still gets 401 on /api/dashboard, /api/accounts and /api/contracts — the same invariant scripts/deploy.sh refuses to ship without. CD becomes tag-to-ship. CI publishes an image to the Gitea registry on a release-* tag and cloud-2 pulls it, so no credential on the shared runner can execute anything on production — by construction rather than by policy. Both halves of deploy.sh's original rule survive: nothing on the runner reaches the host, and a human still decides when it ships. deploy.sh gains a rollback and a public-origin check, and PIG_IMAGE now reaches compose through `sudo env`, without which sudo's env_reset silently resolved every release to pig:local. Tests 141 -> 261. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a6167629cc |
Move from npm to pnpm across the workspace, CI and the image
CI / verify (push) Successful in 3m23s
The monorepo was on npm workspaces. pnpm gives it a content-addressed store shared between the eight packages, a lockfile that records the whole graph rather than a flattened view of it, and — the reason this mattered in practice — `workspace:*`, which makes an internal dependency unambiguous instead of a version range that npm may satisfy from the registry. Mechanics: - `packageManager: pnpm@11.21.0` pins the version; corepack installs it in CI and in the image, so all three environments resolve identically. - The npm `workspaces` array is replaced by `pnpm-workspace.yaml`. pnpm ignores the former, and keeping both would leave two sources of truth. - All six internal dependencies moved to `workspace:*`. - Root scripts use `pnpm -r --if-present` and `pnpm -F <pkg>`. Two findings worth recording, both from running it rather than reading it: `tsx` was a devDependency, but the server runs TypeScript directly in production — the container's command is `pnpm exec tsx apps/api/src/server.ts`. Under npm this was concealed by the runtime stage re-installing tsx by hand after pruning dev dependencies. Under `pnpm install --prod` that sleight of hand stops working and the image simply fails to start. tsx is now declared in `dependencies`, which is what it has always actually been. The first image build failed with ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY. That is not a pnpm bug: it had decided the modules directory was stale and wanted confirmation before deleting it, which a non-interactive build cannot give. The trigger was the host's `node_modules` reaching the build context — there was no `.dockerignore` at all. pnpm's tree is symlinks into a content-addressed store, so copying it into an image produces dangling links and a directory pnpm rightly considers corrupt. Fixed by adding `.dockerignore` and setting `CI=true`, which is required in any non-interactive pnpm build. `esbuild` is denied install scripts via `allowBuilds`. Its platform binary arrives through the optional dependency `@esbuild/linux-x64` and the postinstall only verifies it; confirmed by running the binary directly, which reports 0.25.12. Verified under pnpm: typecheck clean, 150 tests / 0 failures, e2e passes, web builds. The image was built and booted against a real Postgres — health ok, `/api/dashboard` 401 with an issuer configured, `/` and `/capacity` serve the SPA, `/og.png` serves as image/png, and the migrator runs from the pruned runtime stage. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
853bde2265 |
Build the agent-native compute CRM platform
CI / verify (push) Successful in 3m6s
|
||
|
|
c747eb2aa7 |
Add deployment: Dockerfile, compose, proxy config, and docs
One container plus a Postgres behind any TLS-terminating proxy. Nothing is specific to a particular host. The app and API are served from a SINGLE origin. This is not tidiness: browser auth sessions live in per-origin storage, so splitting them across two hostnames makes sign-in loop in a way that presents as a server fault. The short alias redirects rather than serving a second origin. Two safety properties verified by running the image, not by reading the code: - With NODE_ENV=production and no SUPABASE_URL, the process refuses to start and says why. Serving the whole CRM unauthenticated is a worse outcome than failing to deploy, so the failure is deliberate and loud. - In production the development auth bypass does not apply: an unauthenticated request to /api/dashboard returns 401 rather than adopting the first user in the table. The Dockerfile typechecks all six packages as a build gate, so a deploy that does not compile fails at build time rather than in front of a user. Runtime runs unprivileged as `node`, and Postgres is not published to the host. Docs cover the ontology and why it is shaped this way, agent connection for Claude Code / Codex / prime-agent / Buzz, and the provenance rules governing seed data about real people — including how to have your record removed. Verified: image builds, container reports healthy, serves the SPA, enforces auth, and the production guard exits non-zero. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |