P03 — capability supply graph
Lane: S1-L2 / ACTIONIST-S1-L2-SUPPLY · Part: P03 only · Run: 2026-08-27-sprint-1-fable Observed: 2026-08-27 · Mode: research only · UNEXECUTED · NOT_ADMITTED · admitted blocks 0 Model note: lane began on Fable 5[1m] and continued on Opus 5[1m] at operator direction mid-run; all subagents ran Opus throughout.
1. Headline
Three findings change what this project should do next. All three contradict a number the project was previously carrying.
1. Client portals have essentially no open-source supply — the apparent supply was a tagging artifact. The survey initially showed nineteen rows tagged portal, six of them permissive and maintained. Reading each repository's actual authentication surface rather than its description: BookStack is a wiki, Redoc renders API documentation, Appsmith, react-admin, refine, Budibase and ToolJet are internal-tool frameworks for trusted staff, and cal.diy, Easy!Appointments, Rallly and Tymeslot expose anonymous public booking forms — which is not the same thing as an authenticated external identity with scoped read. The archetype definition is specific: untrusted external identity + scoped read + request submission, "an authorization product before it is a UI."
Seventeen of nineteen rows fail that bar. Portal now holds 2 rows, both copyleft, both reference, and 0 permissive+candidate: open-formulieren/open-forms (EUPL-1.2 — the one unambiguous instance, with external citizen authentication, scoped prefill of that identity's own data, and submission routed to a back office) and nextcloud/server (AGPL-3.0 — genuine external-share identity, but a file-sharing product rather than a purpose-built portal). Portal is the hardest gap on the shelf, it is demanded as a secondary archetype by 6 of 17 industries, and loose tagging had hidden that completely.
2. Case/workflow has a clean ENGINE layer and no clean PRODUCT layer — on any stack. This finding was revised twice, and the second revision corrected my own headline.
Phase 8 concluded the case/workflow spine had no clean supply, under a JS/TS filter. Off that filter I found flowable/flowable-engine (Apache-2.0, implements CMMN, the case-management standard) and kiegroup/jbpm (Apache-2.0), and reported that case/workflow "has mature, permissive, service-shaped supply on the JVM." That was half wrong. The product-versus-primitive pass (§11) shows both are primitive: BPMN/CMMN engine libraries you embed in a Java application, with no UI, no case surface and no identity. Camunda is the same, and ships its Operate and Tasklist UIs as separate non-open components.
Across all 13 case/workflow rows: 7 runnable products exist and every one is strong-copyleft, weak-copyleft or open-core. Clean products: zero. The only permissive candidates are engines.
The accurate claim is therefore stronger and more useful than either previous version: the case/workflow engine layer is solved under permissive terms; the product layer — case UI, party model, deadline surface, document set — is what Actionist would build. That is a much better-scoped build than "build a workflow system," because the hardest computational part (a correct state machine with timers and CMMN semantics) can be borrowed outright.
3. The large repository corpus is resolved as an interface and unavailable as an asset. The ~1.3M-repo index the operator remembers is not local. It is the Foundry engine on the Mac mini, reached through a read-only sqlite3 ?mode=ro client over Tailscale ssh. I verified this myself rather than accepting the subagent's report: the skill exists (foundry, 24,791 B, executable), and foundry status --json returns ENGINE UNREACHABLE (mac-mini): ssh: connect to host [private address redacted] port 22: Network is unreachable. Assumption A20 must therefore split three ways — the index exists (confirmed), "local" is refuted, "currently queryable" is refuted. Every size figure for it (1.3M, 1.36M, 850k/80k) remains an unverified claim, and no shelf row depends on one.
RETRACTED — a fourth claim I made here was false. I originally reported that foundry status exits 0 while printing ENGINE UNREACHABLE, contradicting its documented exit 4. That is wrong. The command returns exit 4 exactly as documented, with and without --json. My probe piped output through head and then read $?, which captured head's exit status rather than foundry's. CENA's independent probe caught it and a direct re-test confirmed it. There is no defect in the skill, and the engine-unreachable finding itself is unaffected. Recorded as R-01 in lane-state.json; the lesson is never to read $? through a pipe.
2. What was built
| Artifact | Content |
|---|---|
top-repos.jsonl | 200 live-verified OSS records + summary. 237 repos queried via gh api, 60 licence bodies fetched and read, 24 stale identity redirects caught, 23 capability kinds. |
top-companies.jsonl | 100 commercial capability-supply vendors + summary, across 25 kinds. |
overlap-graph.json | 300 source nodes, 28 kinds, 446 supplies(source, kind) edges, with per-kind commodity/scarce/missing classification. |
innovation-register.jsonl | 100 design hypotheses across 10 categories, top 10 ranked. |
source-register.jsonl | 23 sources across all 7 provenance lanes. |
decision-ledger.json | 10 lane decisions, each with rationale, evidence, falsifier and status; 5 open gates. |
first-principles.md, capability-taxonomy-and-industry-join.md | Synthesis, 24-kind taxonomy, 17-industry join. |
challenge-plan.md / challenge-results.md | Adversarial pass, written before the numbers landed. |
smoke.py, build-overlap-graph.py | Mechanical gate and graph derivation. |
3. Method, and why the shelf is not repo-rowed
Every prior artifact in this project counts repositories. That framing fails in both directions at once. It overcounts common capabilities, because one mature system ships several separable ones — the graph measures this directly: 30 sources supply 3 or more kinds, one supplies 5. It undercounts scarcity for the capabilities we need, because the case/workflow state machine inside a helpdesk is desk-specific and is not a general spine.
So a shelf row is the triple (capability_kind, source, reuse_shape) (decision D-01), and the unit of supply is an edge, not a node. That is what makes portal's collapse from "6 clean candidates" to "0" visible at all.
The counting discipline inherited from the knowledge spine is preserved: 17,000 matrix observations are not 1,700 complete pairs (270 complete at baseline); 8,515 UI identities are not 8,515 usable components (3,506 are source-bearing); a licence badge is not a rights answer.
4. The supply picture
Classification requires both denominators; a kind populated on only one side returns UNDERDETERMINED rather than a verdict (D-04).
Revised by the product-versus-primitive pass (§11). The counts below are the post-split figures. Six kinds moved once "clean candidate" was required to mean a runnable product under a usable licence:
admin_data,formsandsearchfell commodity → scarce;case_workflowande_signfell commodity → product layer missing;field_opsmoved missing → product layer missing. Of 200 rows, 134 are products, 20 frameworks, 46 primitives.
Commodity — 13 kinds (airtable_data, analytics_bi, approvals_workflow, auth_identity, billing, calendar_scheduling, crm, e_commerce, files_documents, inventory, messaging_notifications, notes_docs, project_management). For these the problem is selection, not sourcing. Auth/identity remains the deepest.
Scarce — 6 kinds (admin_data, connectors, forms, lms, search, support_desk). One clean product each, or none with a resale-compatible commercial route. portal is the sharpest: 2 rows, both copyleft, zero clean.
Product layer missing under permissive terms — 3 kinds (case_workflow, e_sign, field_ops). This category did not exist before the split, and it is the most decision-relevant result of the lane. Each has runnable products — 7 for case_workflow, 5 for e_sign, 5 for field_ops — but every one is copyleft, weak-copyleft or open-core. The permissive rows are engines and libraries. You can borrow the hard computational core and must build the product around it.
Underdetermined — 6 kinds. case_workflow has OSS supply and no commercial rows surveyed. Five kinds (AI-agent platform, chat/collaboration, doc generation, email infra, video/meetings) have commercial supply and no OSS rows — these are commercial-native categories where the OSS survey did not look, and that is a survey boundary, not a finding.
4.1 The product-versus-primitive split, and why clean-candidate counts overstate supply
The most generalisable result of this run is not any single kind's number. It is that a "clean candidate" count silently mixes two very different things: a finished product you could run, and a primitive you would have to build a product around.
E-signature makes it concrete. The kind shows 11 rows and 3 clean candidates, which reads as adequate. But every full DocuSign-class product in the file — documenso, docuseal, OpenSign, libresign — is AGPL, and every permissive row is a signing library (node-signpdf, pyHanko, digitorus/pdfsign) that cryptographically signs a PDF and supplies no workflow, no signer identity, and no audit trail. The three "clean candidates" are not three e-signature options; they are cryptographic primitives with the entire product layer missing. Scheduling has the same shape: 15 rows, but calcom/cal.diy is the only permissively licensed full booking product in the field.
So for both kinds the honest classification is "product layer missing under permissive terms" rather than commodity. This is the same failure mode as the connector headline this project already corrected once — quoting 728 services when 113 were OAuth-ready. A raw count answers "how many rows" when the decision needs "how many things I could actually ship."
The overlap graph's clean_oss_candidates column should therefore be read as an upper bound, not a supply figure, until each kind is split product-versus-primitive. That split is done here only for e-signature and scheduling, where it was forced by the evidence. Applying it across all 23 kinds has been dispatched (see §11) and is expected to lower several commodity classifications.
4.2 The inflation pattern, and the counter the shelf needs
Three separate defects in this run were the same mistake wearing different clothes, and all three inflated apparent supply:
| Defect | What the loose count said | What survived scrutiny |
|---|---|---|
| Portal tagging | 19 rows, 6 permissive candidates | 2 rows, 0 clean — 17 failed the archetype bar |
| E-signature / scheduling | 11 and 15 rows, "adequate" | one permissive full product between them |
| Prior connector work | 728 services | 113 OAuth-ready |
The connector correction was made by an earlier phase of this project. This run made the same class of error twice more and caught it twice more. That is not a coincidence to note and move past — it is a structural property of counting rows in a heterogeneous corpus.
The durable fix is that the shelf must carry two separate counters, never one:
rows— how many sources mention this capability. Useful only for discovery breadth.supply_under_usable_terms— how many are a runnable product, under a **licence we
can actually use, that serves the capability as defined**. This is the only number a build-versus-borrow decision may consult.
Portal reads as well-supplied on counter 1 and as the shelf's hardest gap on counter 2. Anyone quoting counter 1 as supply will re-make this mistake, and the report's own classification counts are a counter-1 artifact until §11's pass completes.
Portal is the sharpest instance and worth stating plainly: there is no clean permissive supply for the authorization archetype at all. open-forms is EUPL-1.2, Netherlands-specific and small; nextcloud is AGPL-3.0 and is a file-sharing product wearing a portal feature. The authorization layer — untrusted external identity, scoped read, request submission — is the part Actionist would have to build. That is the single most decision-relevant line in this lane, and it only became visible after the tag was tightened.
5. Rights
Rights class gates disposition, not shelf membership (D-03). Deleting copyleft rows would make the shelf look emptier than reality and hide two legitimate routes — keep the source behind a network boundary as an intact service, or take the architecture as a pattern. The challenge pass confirmed these rows are worth keeping: 58 of 65 strong-copyleft rows are actively maintained, not abandonware.
Licence reading, not licence badges, did real work here. Sixty licence bodies were fetched and read. Two cases show why the API field is insufficient: jBPM returned an empty /license endpoint because its root file is named LICENSE-Apache-2.0.txt — an API-field read would have recorded a permissive project as unlicensed. Camunda has no root LICENSE at all and a licenses/ directory carrying both APACHE-2.0.txt and a proprietary CAMUNDA-LICENSE-1.0.txt; it is correctly held at reference pending per-module resolution rather than promoted because an Apache file exists. This extends the Phase-8 result, where direct re-query flipped 5 of 17 rows off a permissive assumption.
What is not established: no dependency tree or SBOM was scanned, so every permissive reading is top-level only; AGPL network-use against a resale model is a counsel question; no row is cleared, admitted or approved.
6. The 17-industry join
Six kinds are needed by all 17 industries — auth/identity, party records, notifications, forms/intake, files/documents, reporting. A second band is needed by twelve or more: case/workflow, approvals, portal, document generation, scheduling. The rest cluster by industry.
Re-derived directly from the Phase-8 table rather than carried forward: 17 industries, case_workflow primary for 6 and secondary for 3 (touching 9 of 17), portal secondary for 6, marketplace primary and secondary for 0 — so marketplace is correctly excluded from shelf v1.
The uncomfortable overlap: the two kinds carrying the most demand concentration, portal and case/workflow, are precisely the two with the least clean supply. Supply is abundant where the denominator needs it least.
7. Local estate and provenance
Real local supply today is ~3,506 rights-gated source-bearing UI components plus SISOCRM first-party code and its donor integrations. Everything larger is rights-gated (2 licence fields across 11,549 third-party records), source-free, process-only, or behind the unreachable engine. AutoSaaS contributes process and gates, zero supply — its own maturity note says no SaaS has been generated by it yet.
SISOCRM remains the most valuable local evidence because it is the only place where these decisions have actually been made against a shipping product: Twenty record surfaces owned, Plane/Documenso/Papermark/Activepieces kept as downstream services, one Postgres with separate schemas per donor and cross-donor joins only through events into SISO-owned tables. Its transferable rule is worth quoting: the failure mode is owning the security-critical parts while renting the parts that define your product.
The user-provided lane is empty, and that is a finding. No enumerated SaaS or repository list exists in the paths checked (client/ in full, PROJECT.md, README.md, research/ top level). What exists is one repo URL — the client's own empty Actionist-AppSDK — and three prior-art names. The client's own words, "Probably 10 open source repos for this already. Just dno where," are a hypothesis, not supply. The lane is kept distinct and empty rather than backfilled from our own surveys (D-08). A real list is client input and must be requested through CENA.
8. What would falsify this
- F2 (portal/case_workflow scarcity): a permissive, maintained, domain-general portal kit
or JS/TS case spine surfacing would move those kinds to commodity and weaken the build case. The case/workflow half of F2 already partially fired — Flowable and jBPM are exactly that, on the JVM.
- F4 (commodity claims): licence-body reads flipping ≥20% of "permissive" commodity rows,
as happened on 29% of the Phase-8 tranche.
- Portal correction itself: if the archetype bar is too strict — if clients would accept
an admin framework with an external-user role as a portal — then portal supply is adequate and finding 1 is wrong. That is a product judgement, and S1-L3 owns it.
9. Limitations
Capability-kind assignment is judgement, and this run proved it is fallible — the portal tag was wrong on 13 rows until challenged. Quality signals are observable proxies (maintenance recency, CI, containerization, docs), not quality measurements. No repository was cloned, built, executed or benchmarked. No adaptation cost has been measured anywhere in this project, so every cost statement is an estimate. The commercial file remains largely recall-based, and the pass meant to fix that produced nothing: 17 of 100 rows are first-party-verified, and a re-audit at lane close found zero rows carrying either field the verification pass was specified to write. So 83 vendor rows rest on recalled knowledge in exactly the three fields that decide build-versus-buy — embed mode, white-label rights and pricing model. A wrong "white-label: yes", a missed enterprise-tier embedding gate, or undetected per-end-user pricing would each be a commercial error in front of the client. Those fields must be re-read from first-party pages before any client-facing use. This is blocker B-03 and it is the weakest part of this packet.
10. What this lane hands to others (read-only)
To P04 (repo-to-block): a prioritised queue with reuse shapes pre-decided, so conversion never starts from raw search again. To S1-L1 (demand): the scarcity index per kind, which is a pricing and positioning input. To S1-L3 (experience): the portal archetype-bar question. To the coordinator: the A20 three-way split (D-06) and the foundry exit-code defect (D-09), neither applied here because this lane writes neither knowledge/ nor shared skills.
11. In flight at lane close
One pass is running rather than merely recommended: the product-versus-primitive split across all 23 capability kinds (Opus subagent, dispatched 2026-08-27). It tags every one of the 200 rows product / framework / primitive from repository structure rather than self-description, then recomputes each kind's classification. A kind with product rows but zero clean products — every runnable product copyleft or source-available — becomes product_layer_missing_under_permissive_terms rather than commodity.
Outputs expected: product-primitive-split.json, plus supply_tier and supply_tier_evidence on all 200 rows. smoke.py check 7 binds automatically when that file appears: it validates the tier vocabulary, requires per-row evidence, checks each kind's tier counts sum to its total, and verifies every revised_classification is consistent with its own numbers — a kind cannot be labelled commodity while showing fewer than two clean products.
Result — the pass completed and the provisional figure did fall. Of 200 rows: 134 product, 20 framework, 46 primitive. Six kinds were reclassified and commodity dropped from 17 to 13. admin_data, forms and search fell to scarce once "clean candidate" required a runnable product; case_workflow and e_sign fell to product-layer-missing; and field_ops moved from missing to product-layer-missing, which is a more accurate and less bleak description — products exist there, they are simply all copyleft.
The pass also refuted my own Finding 2 (see §1), which is the outcome an honest split should be capable of producing. smoke.py check 7 bound on the new file and passed: tier vocabulary valid on all 200 rows, per-row evidence present, per-kind arithmetic ties out, and no kind is labelled commodity while showing fewer than two clean products.
Sprint 1 is not claimed complete. This lane's part remains unpromoted pending coordinator verification.