# PortCo Desk > One portfolio company's period reporting pack, worked four ways in a single sitting: > where it stands against the plan it was underwritten on, whether the unit economics > support that plan, what closes the gap, and what the exit looks like on this trajectory. URL: https://portco-desk.skillsafe.ai/ API tutorial: https://portco-desk.skillsafe.ai/api.html Token panel: https://portco-desk.skillsafe.ai/tokens.html Platform: SkillSafe apps (https://skillsafe.ai/) Model: gpt-terra (currently resolves to gpt-5.6-terra) · publisher markup 1000 bps Cost: signing in is required to run a lane; the whole prescan and every prescan export is free. ## What it is for A deal team, an operating partner or a finance lead receives one portfolio company's monthly or quarterly reporting pack. PortCo Desk is the sitting that follows: read the pack against plan, understand the unit economics underneath it, decide what to do about the gap, and re-price the exit. It is analysis of pasted figures. It is **not** investment advice, a valuation, a fairness opinion or an audit, and it never recommends buying, selling or holding anything. ## The four lanes The app has one system prompt and one model, and routes on an explicit `task` field. | `task` | Name | The question it answers | Its extra output block | | --- | --- | --- | --- | | `monitor` | Monitor | Where is the company against the plan it was underwritten on, and what is driving each gap? | `drivers` | | `unit` | Unit economics | Do the unit economics support the plan, and which ratio is load-bearing? | `metrics` | | `plan` | Value creation | What closes the gap, and what happens in the first hundred days? | `levers` | | `returns` | Exit case | On this trajectory, what does the exit look like, and does it clear the hurdle? | `cases` | The intended pipeline is `monitor` → `unit` → `plan` → `returns`, and each result carries a digest of its conclusions into the next lane as `carryover`. Every lane stands alone: opening `returns` first still returns a complete answer. ## The input contract One JSON object. `task` is the only field that changes the contract. - `task` — `monitor` | `unit` | `plan` | `returns` - `pack` — the reporting pack as text: a metrics table of actuals against plan, usually with a prior-year column. The primary input for every lane. - `deal` — the deal terms as `key: value` lines. Optional, but the exit case needs them. - `stage` — `buyout` | `growth` | `carve-out` | `platform` | `venture` | `unknown` - `emphasis` — `general` | `revenue` | `margin` | `cash` | `exit` - `context` — free text about the period. Attributed by the model, never treated as a figure. - `carryover` — a digest of the previous lane's conclusions. Empty on a first run. - `prescan_facts` — the browser's own arithmetic, handed over as ground truth: `stats`, `variances`, `derived`, `returns`, `sensitivity` and `flags`. The object is the request body itself. There is no `input` wrapper — and note that sending one anyway is **not** rejected: `{"input": {...}}` returns 200 with a plausible `hold_credits` (1,719 against the correct body's 1,833), because the platform passes the object straight through, so the model never sees `task` and answers whichever lane it guesses. Verified live on 2026-08-19. The same is true of an invented app-slug header: a bogus value returns 200 and changes nothing. There is no error to catch. ## The output contract One JSON object, identical in every lane apart from the extra block: `lane`, `title`, `company`, `period`, `verdict` (`on-track` | `watch` | `at-risk`), `headline`, `summary`, `checks[]` (exactly the lane's eight named checks, in order, each `pass` | `warn` | `fail` | `unknown`), `findings[]` (`id` PD-nnn, `severity`, `area`, `metric`, `detail`, `so_what`, `action`), `coverage_check[]` (one entry per prescan flag id, `confirmed` or `set-aside`), `data_gaps[]`, `next_steps[]`, `disclaimer`, plus the lane's own block. ## What the browser does for free, before any model call This is most of the app, and it costs nothing: - **Reads the table.** The delimiter (pipe, tab, semicolon, comma, aligned space) is chosen on evidence across the whole paste, not off the first line. Quoted cells survive. Rule rows are ignored. The actual, plan and prior-year columns are resolved from the header; where there is no header the assumption is made explicit rather than hidden. - **Reads the figures the way packs write them.** `(1,234)` is negative, `$1.2m` is 1,200,000, `12.4%` is a percentage, `8.5x` is a multiple, `1 234,56` is a European decimal. - **Knows over fifty standard line items** whatever the pack calls them — `S&M`, `S&M spend`, `Total S&M costs` all resolve to one id — and knows which direction each should move. A churn line under plan is favourable; a revenue line under plan is not. - **Varies percentage lines in percentage points**, not as a percentage of a percentage. - **Derives every unit economic the rows allow**: gross margin, EBITDA margin, ARPA, CAC, LTV, LTV/CAC, CAC payback, NRR, GRR, net new ARR, growth, Rule of 40, burn multiple, magic number, cash conversion, net leverage. Each records the rows it rests on and the band it is judged against. A ratio whose inputs are absent is reported as **not derivable** and never estimated. - **Completes the entry triangle** — EV, EBITDA, multiple, equity, net debt — wherever two sides are known, and states each derivation. - **Solves the IRR by bisection** over the annual flow vector, reporting the iteration count and the residual, and says what one more year of hold costs at the same money multiple. - **Attributes the value-creation bridge** to EBITDA growth, multiple change and debt paydown, and reports the unexplained residual rather than absorbing it into one of the three. - **Computes a five-by-five exit sensitivity grid** over exit multiple and exit EBITDA, every cell computed rather than interpolated. - **Raises deterministic flags** the model must then reconcile one by one: a margin that does not recompute from the lines above it, an ARR movement that does not close to closing ARR, a plan column that matches the actuals on most rows, receivables outgrowing revenue, an EBITDA beat that did not reach free cash flow, a covenant with no headroom, a CAC payback longer than the hold that remains, negative net new ARR, and multiple expansion presented as a result rather than an assumption. ## How the model is held to it Every prescan flag must come back either `confirmed` or explicitly `set-aside`, and the page names any flag the model dropped and any id it invented. The unit lane must judge every ratio the browser derived, and the page reports any it skipped. The returns lane's base case must reproduce the browser's own MOIC and IRR, and the page checks that it did. Checks are rendered against the lane's exact roster, so a check the model skipped shows as *not returned* rather than shortening the table. ## Sources A derived work of four agent skills published in the Anthropic financial-services plugin repository, credited in the app footer and in its description: - `@anthropics/portfolio-monitoring` — the `monitor` lane - `@anthropics/unit-economics` — the `unit` lane - `@anthropics/value-creation-plan` — the `plan` lane - `@anthropics/returns-analysis` — the `returns` lane Not affiliated with those skills' authors, and not endorsed by them. ## Privacy The pack is read in your browser. Nothing is uploaded until you run a lane, and then only for that one analysis. History is stored in the declared `analyses` collection on your own SkillSafe account, mirrored in this browser for instant paint.