Shoot the README screenshots, and make them regenerable
The screenshot section had been a placeholder since the shell was rebuilt as three panes, because there was no cheap way to re-shoot and a stale image is worse than no image. So this ships the capture, not just the captures: scripts/screenshots.mjs takes all ten pages at 1440x900 and 393x852, in light and dark, and screenshots-encode.py halves and re-encodes them to WebP — 16MB of PNG becomes 1.9MB in the tree. Two things would silently ruin a run, and the script exists to encode both. Seeding localStorage['pig.themeMode'] is not enough: the appearance preference is authoritative server-side and adopted after hydration, so every dark capture snapped back to light a beat after first paint. The /api/me/profile response is rewritten instead. And pig.sidebarOpen / pig.piggyDockOpen are per-device, so whatever the last human left behind would otherwise leak in. The run also fails on a wrong theme, a horizontal scrollbar or a console error — three things a screenshot cannot show you. Piggy is not pictured mid-conversation. It is off by default and no inference credential exists, so such an image would be a staged transcript rather than a capture. The README says that rather than implying the feature is missing. Correcting what the README asserted while shooting against the running code: read authorisation IS enforced — createReadGuardRoutes is mounted ahead of the feature routes, and routes/learn.ts and routes/activities.ts are mounted too, so only the HubSpot pair is still unreachable. The remaining read gap is that a grant cannot be narrowed, there being no row-level team filter in the query layer. Counts refreshed against the tree: 275 tests, ~47k lines, 14 migrations. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -60,18 +60,102 @@ for, not because they are universal.
|
||||
|
||||
## Screenshots
|
||||
|
||||
> **Placeholder — fresh captures needed.** The application shell was rebuilt as
|
||||
> a three-pane layout (header, collapsible sidebar rail, docked Piggy) and
|
||||
> every screenshot taken before that redesign now misrepresents the product.
|
||||
> Rather than ship misleading images, this section is deliberately empty until
|
||||
> the new shell is re-shot at desktop and at 393px, in light and dark.
|
||||
>
|
||||
> Pages to capture: `/` Overview, `/margin`, `/capacity` (the matcher),
|
||||
> `/calendar`, `/piggy` docked beside a record.
|
||||
Captured against the current shell — header, collapsible sidebar rail, docked
|
||||
Piggy — running locally on the seed plus demo book (`db:seed` and `db:demo`), so
|
||||
every number below is computed by the code in this repository rather than drawn.
|
||||
Records prefixed `DEMO —` are fictional; the rest are the sourced, cited seed.
|
||||
|
||||
<!-- TODO(screenshots): add docs/screenshots/{overview,margin,capacity-match,calendar}.png
|
||||
— post-redesign, light and dark, desktop and 393px. Do not reuse the
|
||||
pre-redesign pig-*.png audit captures. -->
|
||||
Each image follows your own system theme. Both themes are shown explicitly
|
||||
further down, and [the full gallery](docs/screenshots.md) has all ten pages at
|
||||
1440px and 393px, in light and dark. Desktop captures are the 1440×900 viewport
|
||||
rather than the full scroll height — what you see is what fits above the fold.
|
||||
`node scripts/screenshots.mjs` re-shoots the set.
|
||||
|
||||
**Overview** — margin, sold ratio and idle capacity across the book, with the
|
||||
commitments you are paying for and not selling ranked by cost exposure.
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/overview-desktop-dark.webp">
|
||||
<img src="docs/screenshots/overview-desktop-light.webp" alt="PIG Overview: gross margin $675,871.37, 79.1% sold ratio, 1.3M idle GPU-hours, and a ranked list of capacity bought and unsold.">
|
||||
</picture>
|
||||
|
||||
**Margin** — revenue from what was sold against the *full* cost of what was
|
||||
bought, per commitment. `Cost covered` and a break-even price are the two states
|
||||
that matter; charging only the allocated share of cost would report a healthy
|
||||
margin on a block that is losing money.
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/margin-desktop-dark.webp">
|
||||
<img src="docs/screenshots/margin-desktop-light.webp" alt="PIG Margin: revenue $12.4M against $11.7M of full committed cost, broken down by commitment with sold ratio, cost per hour and break-even price.">
|
||||
</picture>
|
||||
|
||||
**Capacity → Match a requirement** — the matcher. Ask what a customer needs and
|
||||
PIG scores it against capacity already under commitment, saying why each block
|
||||
fits, and hands you straight to the allocation that records the sale.
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/capacity-match-desktop-dark.webp">
|
||||
<img src="docs/screenshots/capacity-match-desktop-light.webp" alt="PIG capacity matcher: a requirement for 64 H100_80GB with high-speed interconnect, scored against two commitments at 64% and 58% fit with an Allocate this capacity action on each.">
|
||||
</picture>
|
||||
|
||||
**Growth** — deterministic attention scores over customer paper, deal activity
|
||||
and sold or reserved capacity. Every point is an explained signal with its
|
||||
sources named; nothing here is a model's guess at a win probability.
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/growth-desktop-dark.webp">
|
||||
<img src="docs/screenshots/growth-desktop-light.webp" alt="PIG Growth: accounts ranked by attention score, each tagged deployed, expansion candidate, at risk or coverage gap, with the scoring signals listed underneath.">
|
||||
</picture>
|
||||
|
||||
**Calendar** — what closes, what renews, what expires and when capacity lands,
|
||||
projected from the records that already carry the dates. Export authorisations
|
||||
expire on this timeline too, because an expired one converts lawful business
|
||||
into unlawful business.
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/calendar-desktop-dark.webp">
|
||||
<img src="docs/screenshots/calendar-desktop-light.webp" alt="PIG Calendar for 2026-Q3: weighted pipeline, deals closing, renewals, obligations due and authorisations expiring, above a quarter timeline with one lane per kind.">
|
||||
</picture>
|
||||
|
||||
### Light and dark
|
||||
|
||||
Theme is a stored preference that follows a person between devices, resolved
|
||||
before first paint by an inline script so dark-mode users never get a white
|
||||
flash. Both tunings of the accent palette are defined in `@pig/core` and applied
|
||||
as CSS variables at runtime, so there is one definition of each colour.
|
||||
|
||||
The demand pipeline, in both. Legal sits second rather than last, because MSA
|
||||
and DPA execution gates delivery rather than closing the deal — most CRMs put
|
||||
contracts at the end of the funnel and are wrong about it for this market.
|
||||
|
||||
*Light:*
|
||||
|
||||
<img src="docs/screenshots/demand-desktop-light.webp" alt="PIG demand pipeline in light mode: ten stages from qualification through legal, scoping, proposal, procurement, POC and deployment, with deal cards showing ACV, product line and MSA/DPA badges.">
|
||||
|
||||
*Dark:*
|
||||
|
||||
<img src="docs/screenshots/demand-desktop-dark.webp" alt="The same demand pipeline in dark mode.">
|
||||
|
||||
Side by side at 393px, where both tunings have to survive a smaller surface:
|
||||
|
||||
| Supply pipeline · light | Supply pipeline · dark |
|
||||
| --- | --- |
|
||||
| <img src="docs/screenshots/supply-mobile-light.webp" alt="Supply pipeline at 393px in light mode"> | <img src="docs/screenshots/supply-mobile-dark.webp" alt="Supply pipeline at 393px in dark mode"> |
|
||||
|
||||
### Mobile
|
||||
|
||||
PIG is responsive to 393px — the sidebar becomes a bottom tab bar, tables become
|
||||
cards, and the safe-area insets are handled. It is not a native app.
|
||||
|
||||
| Overview · light | Overview · dark | Margin · dark |
|
||||
| --- | --- | --- |
|
||||
| <img src="docs/screenshots/overview-mobile-light.webp" alt="PIG Overview at 393px in light mode, with a bottom tab bar"> | <img src="docs/screenshots/overview-mobile-dark.webp" alt="PIG Overview at 393px in dark mode"> | <img src="docs/screenshots/margin-mobile-dark.webp" alt="PIG Margin at 393px in dark mode, the commitment table reflowed into cards"> |
|
||||
|
||||
Piggy is deliberately not pictured mid-conversation. It is off by default
|
||||
(`PIGGY_ENABLED=false`, and the Compose service sits behind a profile), and
|
||||
showing it answering would mean staging a transcript rather than capturing one.
|
||||
What it may and may not do is described under [the agent
|
||||
surface](#the-agent-surface).
|
||||
|
||||
## The two-sided data model
|
||||
|
||||
@@ -245,7 +329,7 @@ half-working.
|
||||
|
||||
## Architecture
|
||||
|
||||
A pnpm monorepo. Around 45k lines of TypeScript including tests, 261 tests
|
||||
A pnpm monorepo. Around 47k lines of TypeScript including tests, 275 tests
|
||||
across five packages, green CI.
|
||||
|
||||
```
|
||||
@@ -257,9 +341,9 @@ apps/
|
||||
cli/ `pig`, the HTTP surface for scripts and agent kernels
|
||||
packages/
|
||||
core/ Ontology, permissions, margin arithmetic, palette — no I/O
|
||||
db/ Drizzle schema (47 tables), 13 migrations, seed and demo data
|
||||
db/ Drizzle schema (47 tables), 14 migrations, seed and demo data
|
||||
prime/ Typed client for the Prime Intellect compute API
|
||||
docs/ ontology.md, build-plan.md, agents.md, seed-data.md
|
||||
docs/ ontology.md, screenshots.md, build-plan.md, agents.md, seed-data.md
|
||||
deploy/ Caddyfile example, autodeploy units, deployment notes
|
||||
```
|
||||
|
||||
@@ -322,11 +406,15 @@ required to hold it is what separates the roster from the cost book.
|
||||
`economics:read` is the one that matters — supplier cost per GPU-hour and
|
||||
break-even price *are* the business.
|
||||
|
||||
⚠️ **The read half is written and tested but not yet enforced.** The policy
|
||||
table (`apps/api/src/routes/read-guards.ts`) and its middleware exist, and
|
||||
`read-governance.test.ts` fails when a GET appears that no rule covers — but
|
||||
`createReadGuardRoutes` is not mounted in `app.ts`, so today every
|
||||
authenticated member can read the whole book including cost. See
|
||||
The read half is enforced. The policy table lives in
|
||||
`apps/api/src/routes/read-guards.ts` and `createReadGuardRoutes` is mounted in
|
||||
`app.ts` **before** the feature routes — Hono runs matched handlers in
|
||||
registration order, so a guard registered after its route would return 200 while
|
||||
looking correct. `read-governance.test.ts` pins that ordering in both
|
||||
directions, and fails when a GET appears that no rule covers, so a new read
|
||||
endpoint cannot ship ungoverned by accident.
|
||||
|
||||
What it still cannot do is filter *within* a grant: see
|
||||
[limitations](#what-is-not-built-yet).
|
||||
|
||||
## The agent surface
|
||||
@@ -399,7 +487,7 @@ see [docs/agents.md](./docs/agents.md).
|
||||
|
||||
CI is Gitea Actions, one sequence, about two minutes. It typechecks every
|
||||
package, applies the migration chain **twice** to a real empty Postgres, asserts
|
||||
the seed is idempotent, runs 261 unit tests and the critical-path E2E, boots the
|
||||
the seed is idempotent, runs 275 unit tests and the critical-path E2E, boots the
|
||||
server and curls it, builds the front end, checks the inline theme script still
|
||||
hashes to the value the proxy's CSP allows, and builds the Docker image.
|
||||
|
||||
@@ -428,20 +516,16 @@ one thing an on-call needs at 04:00.
|
||||
|
||||
Said plainly, because you are going to grep the repo anyway.
|
||||
|
||||
**Read authorisation is not enforced.** As above: the policy, the middleware
|
||||
and the governance test all exist; the router line that mounts them does not.
|
||||
Any authenticated member can currently read cost and margin regardless of team
|
||||
or role. This is the most significant gap in the product and it is one line in
|
||||
`app.ts`.
|
||||
**Read authorisation is enforced, but only at the grant.** A capability is held
|
||||
or it is not. Once `economics:read` is held, it returns every commitment's cost
|
||||
— there is no filter that narrows it to one team's book, because no row-level
|
||||
team filter exists anywhere in the query layer. That is the gap to close before
|
||||
PIG serves a company where "supply can see supply's costs" is a requirement.
|
||||
|
||||
**Four route modules are written, tested and never mounted.**
|
||||
`routes/read-guards.ts`, `routes/learn.ts`, `routes/hubspot.ts` and
|
||||
`routes/hubspot-webhook.ts` are all absent from `app.ts`. Consequences: the
|
||||
**Learn** page is in the navigation and its API answers 404, and the HubSpot
|
||||
integration — OAuth, connections, sync jobs, webhook verification, seven
|
||||
`hubspot_*` tables — is unreachable. `routes/activities.ts` is likewise
|
||||
unmounted, but harmlessly: an older inline `POST /api/activities` in `app.ts`
|
||||
still serves it.
|
||||
**The HubSpot integration is written, tested and never mounted.**
|
||||
`routes/hubspot.ts` and `routes/hubspot-webhook.ts` are both absent from
|
||||
`app.ts`, so OAuth, connections, sync jobs, webhook verification and seven
|
||||
`hubspot_*` tables are all unreachable from the running server.
|
||||
|
||||
**Six of the eight declared agent task kinds are never enqueued.** The worker
|
||||
is complete and generic, but only `enrich_account` and `enrich_contact` are
|
||||
@@ -478,6 +562,7 @@ responsive to 393px; it is not a native app.
|
||||
|
||||
- **[AGENTS.md](./AGENTS.md) — start here if you are joining this codebase.**
|
||||
Architecture rules, the traps that have already bitten, and conventions.
|
||||
- [Screenshots](./docs/screenshots.md) — every page, at 1440px and 393px, light and dark
|
||||
- [Ontology](./docs/ontology.md) — the domain model, and why it is shaped this way
|
||||
- [Build plan](./docs/build-plan.md) — what shipped, what remains, in dependency order
|
||||
- [Agent integration](./docs/agents.md) — MCP clients and the CLI
|
||||
|
||||
Reference in New Issue
Block a user