@liftleadershipgroup/lifty 0.1.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -0
- package/bundle/manifest.json +43 -0
- package/bundle/skill/SKILL.md +127 -0
- package/bundle/skill/references/interview-contract.md +104 -0
- package/bundle/skill/references/onboarding-draft.schema.json +266 -0
- package/bundle/skill/scripts/write-onboarding-draft.mjs +510 -0
- package/dist/cli.js +2890 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# LIFTY installer
|
|
2
|
+
|
|
3
|
+
Installs the public `lifty-onboarding` skill for one supported agent at a time.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx --yes @liftleadershipgroup/lifty@next init
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Project scope is the default. Select an agent explicitly with `--agent cursor`,
|
|
10
|
+
`--agent claude-code`, or `--agent codex`; use `--global` only when you intend to
|
|
11
|
+
install into that agent's user-level skill directory.
|
|
12
|
+
|
|
13
|
+
The package embeds and verifies its onboarding bundle. Installation itself
|
|
14
|
+
performs no network, authentication, protected-tool, telemetry, or data-send
|
|
15
|
+
operation. Run `lifty status`, `lifty update`, `lifty rollback`, or
|
|
16
|
+
`lifty uninstall` with the same agent and scope flags to manage the
|
|
17
|
+
installation.
|
|
18
|
+
|
|
19
|
+
## Account commands
|
|
20
|
+
|
|
21
|
+
Once the onboarding skill has written `<project>/.lifty/onboarding-draft.json`
|
|
22
|
+
with `status: "ready_for_auth"`, five agent-independent commands take over. They
|
|
23
|
+
accept no `--agent`, `--global`, `--dry-run`, or `--force` flag; `lifty push`
|
|
24
|
+
accepts `--project-dir PATH`.
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
lifty login # browser handoff, stores ~/.lifty/credentials.json (0600)
|
|
28
|
+
lifty whoami # prints the signed-in email and Supabase Auth host
|
|
29
|
+
lifty push # provisions the workspace from the local onboarding draft
|
|
30
|
+
lifty connect hubspot # opens hosted OAuth and waits for portal connection
|
|
31
|
+
lifty logout # revokes the session best-effort and deletes the credentials
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`lifty login` starts a listener on `127.0.0.1` with an OS-assigned port, opens
|
|
35
|
+
`{LIFTY_APP_URL}/cli/auth?state=<nonce>&port=<port>`, and waits up to ten
|
|
36
|
+
minutes for the hosted page to POST `{state, access_token, refresh_token,
|
|
37
|
+
expires_at}` back to `/callback`. Callbacks whose `state` does not match the
|
|
38
|
+
nonce are rejected with 403 and the CLI keeps waiting; exactly one valid
|
|
39
|
+
callback is accepted. Set `LIFTY_NO_BROWSER=1`, or run where no browser can be
|
|
40
|
+
spawned, and the URL is printed for manual use instead. Tokens travel only in
|
|
41
|
+
the loopback POST body. They never appear in a URL, log line, or terminal output.
|
|
42
|
+
|
|
43
|
+
`lifty push` refuses to send anything unless the draft is a real file (never a
|
|
44
|
+
symlink), parses as a JSON object, and records `status: "ready_for_auth"`. It
|
|
45
|
+
calls `POST {LIFTY_API_URL}/v1/workspaces` with the account's JWT, prints the
|
|
46
|
+
returned workspace name, state, and draft digest, and leaves the draft file
|
|
47
|
+
byte-for-byte untouched. `lifty status` reads
|
|
48
|
+
`GET {LIFTY_API_URL}/v1/workspace`. Neither business request sends a Supabase
|
|
49
|
+
API key; Supabase remains the auth provider only. Status never fails when the
|
|
50
|
+
network or configuration is unavailable.
|
|
51
|
+
|
|
52
|
+
`lifty connect hubspot` requires a provisioned workspace. It asks the hosted
|
|
53
|
+
LIFTY API for a ten-minute, one-use connection URL, opens the DigitalOcean
|
|
54
|
+
handoff in the browser, and polls secret-free connection status. The CLI
|
|
55
|
+
accepts only an HTTPS URL on the configured API origin and prints only the
|
|
56
|
+
connected HubSpot portal ID. HubSpot access and refresh tokens are exchanged,
|
|
57
|
+
verified, encrypted, and rotated server-side; no founder environment variable
|
|
58
|
+
or provider credential is required.
|
|
59
|
+
|
|
60
|
+
Credentials live at `~/.lifty/credentials.json`, written atomically through a
|
|
61
|
+
temporary file and rename, mode 0600 inside a 0700 directory. Access tokens are
|
|
62
|
+
refreshed automatically when they are within sixty seconds of expiry; an
|
|
63
|
+
unusable refresh token produces `Your session expired. Run lifty login again.`
|
|
64
|
+
and nothing else.
|
|
65
|
+
|
|
66
|
+
## Endpoint configuration
|
|
67
|
+
|
|
68
|
+
No endpoint setup is required for the hosted LIFTY environment. The package
|
|
69
|
+
embeds the DigitalOcean app/API ingress, Supabase project URL, and Supabase publishable key,
|
|
70
|
+
so a registry install can proceed directly to `lifty login`.
|
|
71
|
+
|
|
72
|
+
`LIFTY_APP_URL`, `LIFTY_API_URL`, `LIFTY_SUPABASE_URL`, and
|
|
73
|
+
`LIFTY_SUPABASE_PUBLISHABLE_KEY` remain explicit overrides for local development
|
|
74
|
+
and staged cutovers. The app URL hosts the browser handoff, the API URL handles
|
|
75
|
+
workspace business operations, and the Supabase values are used only for
|
|
76
|
+
account profile, token refresh, and logout. All three URLs must be HTTPS, except
|
|
77
|
+
on loopback hosts. `LIFTY_SUPABASE_ANON_KEY` remains a compatibility alias.
|
|
78
|
+
|
|
79
|
+
Each managed target has a private ownership receipt at
|
|
80
|
+
`<agent-config-root>/.lifty-onboarding-owner.json`. It contains only installer
|
|
81
|
+
identity, version, provenance, and one-way path digests. It never stores filesystem paths,
|
|
82
|
+
onboarding data, or credentials. The receipt prevents another profile from
|
|
83
|
+
force-claiming the same physical target and is removed when managed ownership
|
|
84
|
+
ends.
|
|
85
|
+
|
|
86
|
+
Lifecycle mutations coordinate through private, checksummed lease files in
|
|
87
|
+
`<agent-config-root>/.lifty-onboarding.lock`. A lease records only a random
|
|
88
|
+
token, process ID, user ID, and creation time. Locks whose owner process is
|
|
89
|
+
demonstrably dead are recovered automatically; live locks remain busy. Corrupt
|
|
90
|
+
or unverifiable lock metadata fails closed. If that happens, inspect the path
|
|
91
|
+
reported by the error and remove it manually only after confirming that no
|
|
92
|
+
LIFTY process is active.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"packageVersion": "0.1.0-next.0",
|
|
4
|
+
"bundleVersion": "0.1.0-next.0",
|
|
5
|
+
"treeDigest": "ac1693c8f0f979fbea64b2581f251dea768249cd4475c27739fcc1d9360cfe48",
|
|
6
|
+
"rootMode": 493,
|
|
7
|
+
"directories": [
|
|
8
|
+
{
|
|
9
|
+
"path": "references",
|
|
10
|
+
"mode": 493
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"path": "scripts",
|
|
14
|
+
"mode": 493
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"files": [
|
|
18
|
+
{
|
|
19
|
+
"path": "SKILL.md",
|
|
20
|
+
"mode": 420,
|
|
21
|
+
"size": 6412,
|
|
22
|
+
"sha256": "8bc20af7ab5c5166dfd33f9dabd5335154d9d316c184048e59a37b1b478252cc"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"path": "references/interview-contract.md",
|
|
26
|
+
"mode": 420,
|
|
27
|
+
"size": 4915,
|
|
28
|
+
"sha256": "5ea57a996a3a7f1c559cd074cc1a45b7ff0909df19647b66e493516cb9004091"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"path": "references/onboarding-draft.schema.json",
|
|
32
|
+
"mode": 420,
|
|
33
|
+
"size": 8022,
|
|
34
|
+
"sha256": "e6e584a4d8c9189d6032d9ab94ed2139eb3d18ff5a8cf8274e90b3a1650ac9a9"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"path": "scripts/write-onboarding-draft.mjs",
|
|
38
|
+
"mode": 493,
|
|
39
|
+
"size": 19114,
|
|
40
|
+
"sha256": "368535d1f798af1246d7dd15593be91aee7fe77f402429eaa83a932c8d6d638c"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lifty-onboarding
|
|
3
|
+
description: Use when a founder has installed LIFTY and wants to define one primary outbound motion, authenticate, provision the hosted workspace, and optionally connect HubSpot.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# LIFTY founder onboarding
|
|
7
|
+
|
|
8
|
+
Build one founder-confirmed targeting draft, save it privately in the active
|
|
9
|
+
project, complete the hosted browser handoff, and provision one LIFTY workspace.
|
|
10
|
+
|
|
11
|
+
## Boundary and completion
|
|
12
|
+
|
|
13
|
+
This flow may research public company information, interview the founder, run
|
|
14
|
+
the installed LIFTY CLI's hosted login, and submit the validated local draft to
|
|
15
|
+
the LIFTY API, and—when the founder asks for it—launch the CLI's reviewed
|
|
16
|
+
HubSpot OAuth handoff. It never asks for or prints tokens, manually installs
|
|
17
|
+
provider apps, launches lead research, activates outreach, or sends anything.
|
|
18
|
+
Configure one primary motion; record every secondary motion as parked.
|
|
19
|
+
|
|
20
|
+
Completion means exactly:
|
|
21
|
+
|
|
22
|
+
- `<active-project>/.lifty/onboarding-draft.json` exists with status
|
|
23
|
+
`ready_for_auth` and remains byte-for-byte local after submission;
|
|
24
|
+
- the local writer has validated the draft and privacy controls;
|
|
25
|
+
- hosted browser authentication has returned successfully to the CLI; and
|
|
26
|
+
- `lifty push` has returned workspace state `ready_for_connections`.
|
|
27
|
+
|
|
28
|
+
When HubSpot connection was part of the founder's request, completion also
|
|
29
|
+
requires `lifty connect hubspot` to report the secret-free portal ID as
|
|
30
|
+
connected. OAuth credentials remain backend-only.
|
|
31
|
+
|
|
32
|
+
Read [the interview contract](references/interview-contract.md) in full before
|
|
33
|
+
starting. Use [the draft schema](references/onboarding-draft.schema.json) as the
|
|
34
|
+
only output shape.
|
|
35
|
+
|
|
36
|
+
## Workflow
|
|
37
|
+
|
|
38
|
+
1. Identify the active project and the founder's company/domain. If either is
|
|
39
|
+
ambiguous, ask one clarifying question.
|
|
40
|
+
2. Before interview questions, research public sources: the company website
|
|
41
|
+
and relevant subpages, public founder/company profiles, case studies,
|
|
42
|
+
reviews, hiring pages, and reputable news. Degrade gracefully when a source
|
|
43
|
+
is unavailable.
|
|
44
|
+
3. Present a short research playback that opens with what could not be learned.
|
|
45
|
+
Label every researched value `inferred`. An inference cannot enter the
|
|
46
|
+
confirmed configuration until the founder explicitly confirms or corrects
|
|
47
|
+
it.
|
|
48
|
+
4. Interview one question at a time. Wait for the answer before asking the
|
|
49
|
+
next question. Reject adjective-only sizing; require a numeric floor and
|
|
50
|
+
the founder's unit. When two operating states are bundled, separate them
|
|
51
|
+
and ask which is the priority. Capture the founder's CTA.
|
|
52
|
+
5. When an answer changes, say what changed. The newest statement replaces the
|
|
53
|
+
older value; never average or merge contradictions.
|
|
54
|
+
6. Run negative playback throughout: name what is excluded, weak, unknown, or
|
|
55
|
+
still inferred and ask the founder to correct it.
|
|
56
|
+
7. Before writing, verify all eight hard gates in the interview contract,
|
|
57
|
+
workspace-specific Decision Maker/Influencer tells, one-rule boundary
|
|
58
|
+
cases, CTA, one primary motion, and parked secondary motions. Continue the
|
|
59
|
+
one-question loop if anything is missing.
|
|
60
|
+
8. Build the JSON defined by the schema. Pass it to the bundled writer over
|
|
61
|
+
process stdin; do not embed draft content in command text, shell history,
|
|
62
|
+
logs, or telemetry:
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
node <skill-root>/scripts/write-onboarding-draft.mjs \
|
|
66
|
+
--project-dir <active-project> --input -
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
9. After a successful write, tell the founder that the draft is private and
|
|
70
|
+
local and that LIFTY will now open the hosted authentication page. Resolve
|
|
71
|
+
the persisted CLI as `<active-project>/.lifty/bin/lifty.mjs`; refuse to use
|
|
72
|
+
it if it is missing, not a real file, or resolves outside the active
|
|
73
|
+
project's `.lifty` directory.
|
|
74
|
+
10. Run the browser handoff and wait for its result:
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
node <active-project>/.lifty/bin/lifty.mjs login
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The founder may need to sign up or sign in and approve the local CLI. Never
|
|
81
|
+
ask them to paste a token or callback payload. If they deny, close the page,
|
|
82
|
+
or the CLI times out, preserve the draft and tell them to rerun login.
|
|
83
|
+
11. Only after login succeeds, submit the draft through the hosted REST API:
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
node <active-project>/.lifty/bin/lifty.mjs push \
|
|
87
|
+
--project-dir <active-project>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Report only the CLI's secret-free workspace name, state, and draft digest.
|
|
91
|
+
Confirm that the returned state is `ready_for_connections`; otherwise stop
|
|
92
|
+
and report the CLI error without attempting provider setup or lead work.
|
|
93
|
+
12. Tell the founder that the workspace is provisioned, the local draft was
|
|
94
|
+
left untouched, and outreach remains off. If HubSpot connection was part
|
|
95
|
+
of their request, run the persisted CLI's hosted OAuth flow:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
node <active-project>/.lifty/bin/lifty.mjs connect hubspot
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The CLI owns the short-lived connection URL, opens it in the browser, and
|
|
102
|
+
waits for secret-free status. The founder approves the reviewed LIFTY app
|
|
103
|
+
in HubSpot; never ask for a client ID, client secret, authorization code,
|
|
104
|
+
access token, or refresh token. Report only the portal ID and connected
|
|
105
|
+
status returned by the CLI. If HubSpot was not requested, describe this as
|
|
106
|
+
the next explicit command and do not run it.
|
|
107
|
+
13. Stop after provisioning or the requested HubSpot connection. Do not launch
|
|
108
|
+
lead work or outreach.
|
|
109
|
+
|
|
110
|
+
## Hard stops
|
|
111
|
+
|
|
112
|
+
- Do not accept "mid-market", "enterprise", or another adjective as size.
|
|
113
|
+
- Do not promote public research because it seems credible. Founder
|
|
114
|
+
confirmation is the boundary.
|
|
115
|
+
- Do not invent an exclusion or tooling policy. Ask for the founder's explicit
|
|
116
|
+
judgment; an empty exclusion set is incomplete.
|
|
117
|
+
- Do not configure two motions. Park the secondary motion in the draft.
|
|
118
|
+
- Do not hand-write the destination file. The bundled writer owns validation,
|
|
119
|
+
symlink refusal, atomic replacement, mode `0600`, and `.lifty/.gitignore`.
|
|
120
|
+
- Do not ask for, paste, echo, log, or summarize access tokens, refresh tokens,
|
|
121
|
+
callback payloads, Supabase keys, or provider credentials.
|
|
122
|
+
- Do not run `push` before the writer succeeds and hosted login returns
|
|
123
|
+
successfully.
|
|
124
|
+
- Do not manually install provider apps or invent provider authorization URLs.
|
|
125
|
+
The only allowed provider handoff is the persisted CLI's `connect hubspot`
|
|
126
|
+
command after a workspace exists and HubSpot was requested.
|
|
127
|
+
- Do not launch lead work, enable outreach, or send anything.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Founder interview contract
|
|
2
|
+
|
|
3
|
+
Use this sequence conversationally. Ask exactly one question, wait for the
|
|
4
|
+
answer, then choose the next question or follow-up. Confirmed research may skip
|
|
5
|
+
the corresponding question; founder-only judgments never skip.
|
|
6
|
+
|
|
7
|
+
## Public research playback
|
|
8
|
+
|
|
9
|
+
Research first. Draft possible answers for the description, named customer or
|
|
10
|
+
look-alike examples, industries, geography, structural signals, and persona
|
|
11
|
+
titles. Each stays `inferred` until explicit founder confirmation.
|
|
12
|
+
When confirmed research enters the configuration, `field` is its dotted output
|
|
13
|
+
path and `value` exactly matches the value stored there. Corrected research
|
|
14
|
+
records the corrected value, never the stale inference.
|
|
15
|
+
|
|
16
|
+
Open playback with the negatives: what was unavailable, ambiguous, excluded,
|
|
17
|
+
or weak. Ask the founder to correct one item at a time. Do not infer numeric
|
|
18
|
+
bounds, exclusions, Decision Maker/Influencer roles or tells, tooling judgment,
|
|
19
|
+
boundary decisions, voice bans, sender intent, or CTA.
|
|
20
|
+
|
|
21
|
+
## Interview sequence
|
|
22
|
+
|
|
23
|
+
### 1. What the company sells
|
|
24
|
+
|
|
25
|
+
1. Confirm a standalone description a smart outsider can understand. Reject
|
|
26
|
+
unexplained category jargon.
|
|
27
|
+
2. Get three named example companies that represent the intended ICP. They may
|
|
28
|
+
be best customers or founder-confirmed look-alikes.
|
|
29
|
+
3. Ask what buyers use instead today.
|
|
30
|
+
|
|
31
|
+
If the founder answers with an abstraction, recover in this order: verticals;
|
|
32
|
+
why those; pain; how the company solves it; three named examples. If the
|
|
33
|
+
founder does not know, derive a hypothesis from their best examples and ask
|
|
34
|
+
them to confirm or correct it. Keep it inferred until they do.
|
|
35
|
+
|
|
36
|
+
### 2. The company that buys
|
|
37
|
+
|
|
38
|
+
1. Ask what was operationally true about the best-fit company that made it
|
|
39
|
+
need the offer. The final structural signal must be publicly observable.
|
|
40
|
+
2. If the answer bundles different states, split them—for example,
|
|
41
|
+
founder-solo versus has-reps—and ask which closes faster or is the primary
|
|
42
|
+
fit. Record the priority; do not silently combine them.
|
|
43
|
+
3. Ask industries in and adjacent industries out. Empty exclusions are not a
|
|
44
|
+
completed judgment.
|
|
45
|
+
4. Force size to numbers: minimum, unit, and—when the founder knows it—a
|
|
46
|
+
ceiling. Probe one notch below the minimum: customer or mistake?
|
|
47
|
+
5. Ask for a look-alike company that appears right but must be excluded and
|
|
48
|
+
the hard, observable tell. Capture at least one disqualifier.
|
|
49
|
+
6. Ask whether existing or competitor tooling is a positive signal, a
|
|
50
|
+
disqualifier, or mixed. Record the founder's rationale; never create a
|
|
51
|
+
policy for them.
|
|
52
|
+
|
|
53
|
+
### 3. The person who buys
|
|
54
|
+
|
|
55
|
+
1. Ask who champions a good deal and who makes the decision.
|
|
56
|
+
2. For each persona, capture whether that person is a `decision_maker` or
|
|
57
|
+
`influencer` for this specific motion and the organizational tell that
|
|
58
|
+
proves it. Titles alone do not decide the role.
|
|
59
|
+
3. Capture at least three first-contact titles for one persona. Ask which
|
|
60
|
+
similar titles waste time and who actually replies at the target size.
|
|
61
|
+
|
|
62
|
+
### 4. Signals and boundary cases
|
|
63
|
+
|
|
64
|
+
Ask what public evidence makes an account timely and what separates the best
|
|
65
|
+
ten from fifty plausible accounts. Translate private judgments into observable
|
|
66
|
+
proxies and confirm the translation.
|
|
67
|
+
|
|
68
|
+
Create two or three cases from the founder's hard rules. Each case changes
|
|
69
|
+
exactly one hard rule while holding all others constant. Ask for `A`, `B`, or
|
|
70
|
+
`pass` and one-line reasoning. Record any refinement as a new statement; the
|
|
71
|
+
newest statement replaces the old one.
|
|
72
|
+
|
|
73
|
+
### 5. Voice and motion
|
|
74
|
+
|
|
75
|
+
1. Ask what outreach the founder deletes instantly. Capture at least one
|
|
76
|
+
forbidden move and the positive voice adjectives they want protected.
|
|
77
|
+
2. Ask whose voice the writing represents and how that sender asks for a
|
|
78
|
+
meeting. Capture an explicit CTA.
|
|
79
|
+
3. Confirm one primary motion. Name each secondary motion, mark it `parked`,
|
|
80
|
+
and do not interview or configure it further.
|
|
81
|
+
|
|
82
|
+
## Negative close
|
|
83
|
+
|
|
84
|
+
Play back the complete configuration in founder language. Start with what is
|
|
85
|
+
out, weak, unknown, parked, or still inferred. Invite corrections one at a
|
|
86
|
+
time. If a correction contradicts an earlier answer, announce the replacement
|
|
87
|
+
and retain the sequence in `founder_statement_history`; never average.
|
|
88
|
+
|
|
89
|
+
## Eight hard gates
|
|
90
|
+
|
|
91
|
+
Keep interviewing until every item exists:
|
|
92
|
+
|
|
93
|
+
1. Standalone company description without unexplained jargon.
|
|
94
|
+
2. Three named example companies matching the ICP.
|
|
95
|
+
3. Numeric size floor plus the founder's unit.
|
|
96
|
+
4. At least one hard disqualifier.
|
|
97
|
+
5. At least one publicly observable structural buy signal.
|
|
98
|
+
6. At least one persona with three or more titles.
|
|
99
|
+
7. Explicit founder judgment of tooling as signal, disqualifier, or mixed.
|
|
100
|
+
8. At least one tone forbidden move.
|
|
101
|
+
|
|
102
|
+
The JSON writer also requires industries in/out, persona roles and tells,
|
|
103
|
+
voice adjectives, sender intent, CTA, negative playback, and one-rule boundary
|
|
104
|
+
cases before it will produce `ready_for_auth`.
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "LIFTY pre-authentication onboarding draft",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"required": [
|
|
6
|
+
"schema_version",
|
|
7
|
+
"status",
|
|
8
|
+
"company",
|
|
9
|
+
"primary_motion",
|
|
10
|
+
"parked_secondary_motions",
|
|
11
|
+
"icp",
|
|
12
|
+
"personas",
|
|
13
|
+
"voice",
|
|
14
|
+
"boundary_cases",
|
|
15
|
+
"negative_playback",
|
|
16
|
+
"research_findings",
|
|
17
|
+
"founder_statement_history"
|
|
18
|
+
],
|
|
19
|
+
"properties": {
|
|
20
|
+
"schema_version": {"const": "1.0"},
|
|
21
|
+
"status": {"const": "ready_for_auth"},
|
|
22
|
+
"company": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"additionalProperties": false,
|
|
25
|
+
"required": ["name", "description", "example_companies"],
|
|
26
|
+
"properties": {
|
|
27
|
+
"name": {"type": "string", "minLength": 1},
|
|
28
|
+
"description": {"type": "string", "minLength": 20},
|
|
29
|
+
"example_companies": {
|
|
30
|
+
"type": "array",
|
|
31
|
+
"minItems": 3,
|
|
32
|
+
"uniqueItems": true,
|
|
33
|
+
"items": {"type": "string", "minLength": 1}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"primary_motion": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"required": ["name", "outcome"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"name": {"type": "string", "minLength": 1},
|
|
43
|
+
"outcome": {"type": "string", "minLength": 1}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"parked_secondary_motions": {
|
|
47
|
+
"type": "array",
|
|
48
|
+
"items": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"additionalProperties": false,
|
|
51
|
+
"required": ["name", "status"],
|
|
52
|
+
"properties": {
|
|
53
|
+
"name": {"type": "string", "minLength": 1},
|
|
54
|
+
"status": {"const": "parked"}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"icp": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"additionalProperties": false,
|
|
61
|
+
"required": [
|
|
62
|
+
"industries_in",
|
|
63
|
+
"industries_out",
|
|
64
|
+
"size",
|
|
65
|
+
"hard_disqualifiers",
|
|
66
|
+
"structural_buy_signals",
|
|
67
|
+
"tooling_judgment",
|
|
68
|
+
"operating_state_split"
|
|
69
|
+
],
|
|
70
|
+
"properties": {
|
|
71
|
+
"industries_in": {
|
|
72
|
+
"type": "array",
|
|
73
|
+
"minItems": 1,
|
|
74
|
+
"items": {"type": "string", "minLength": 1}
|
|
75
|
+
},
|
|
76
|
+
"industries_out": {
|
|
77
|
+
"type": "array",
|
|
78
|
+
"minItems": 1,
|
|
79
|
+
"items": {"type": "string", "minLength": 1}
|
|
80
|
+
},
|
|
81
|
+
"size": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"additionalProperties": false,
|
|
84
|
+
"required": ["floor", "ceiling", "unit"],
|
|
85
|
+
"properties": {
|
|
86
|
+
"floor": {"type": "number", "exclusiveMinimum": 0},
|
|
87
|
+
"ceiling": {"type": ["number", "null"], "exclusiveMinimum": 0},
|
|
88
|
+
"unit": {"type": "string", "minLength": 1}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"hard_disqualifiers": {
|
|
92
|
+
"type": "array",
|
|
93
|
+
"minItems": 1,
|
|
94
|
+
"items": {"type": "string", "minLength": 1}
|
|
95
|
+
},
|
|
96
|
+
"structural_buy_signals": {
|
|
97
|
+
"type": "array",
|
|
98
|
+
"minItems": 1,
|
|
99
|
+
"items": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"additionalProperties": false,
|
|
102
|
+
"required": ["signal", "observable_via"],
|
|
103
|
+
"properties": {
|
|
104
|
+
"signal": {"type": "string", "minLength": 1},
|
|
105
|
+
"observable_via": {"type": "string", "minLength": 1}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"tooling_judgment": {
|
|
110
|
+
"type": "object",
|
|
111
|
+
"additionalProperties": false,
|
|
112
|
+
"required": ["classification", "rationale"],
|
|
113
|
+
"properties": {
|
|
114
|
+
"classification": {
|
|
115
|
+
"enum": ["signal", "disqualifier", "mixed"]
|
|
116
|
+
},
|
|
117
|
+
"rationale": {"type": "string", "minLength": 1}
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"operating_state_split": {
|
|
121
|
+
"type": ["object", "null"],
|
|
122
|
+
"additionalProperties": false,
|
|
123
|
+
"required": ["states", "priority"],
|
|
124
|
+
"properties": {
|
|
125
|
+
"states": {
|
|
126
|
+
"type": "array",
|
|
127
|
+
"minItems": 2,
|
|
128
|
+
"items": {
|
|
129
|
+
"type": "object",
|
|
130
|
+
"additionalProperties": false,
|
|
131
|
+
"required": ["name", "fit"],
|
|
132
|
+
"properties": {
|
|
133
|
+
"name": {"type": "string", "minLength": 1},
|
|
134
|
+
"fit": {"type": "string", "minLength": 1}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"priority": {"type": "string", "minLength": 1}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"personas": {
|
|
144
|
+
"type": "array",
|
|
145
|
+
"minItems": 1,
|
|
146
|
+
"items": {
|
|
147
|
+
"type": "object",
|
|
148
|
+
"additionalProperties": false,
|
|
149
|
+
"required": ["name", "role", "titles", "tell"],
|
|
150
|
+
"properties": {
|
|
151
|
+
"name": {"type": "string", "minLength": 1},
|
|
152
|
+
"role": {"enum": ["decision_maker", "influencer"]},
|
|
153
|
+
"titles": {
|
|
154
|
+
"type": "array",
|
|
155
|
+
"minItems": 3,
|
|
156
|
+
"uniqueItems": true,
|
|
157
|
+
"items": {"type": "string", "minLength": 1}
|
|
158
|
+
},
|
|
159
|
+
"tell": {"type": "string", "minLength": 1}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
"voice": {
|
|
164
|
+
"type": "object",
|
|
165
|
+
"additionalProperties": false,
|
|
166
|
+
"required": ["adjectives", "forbidden_moves", "sender_intent", "cta"],
|
|
167
|
+
"properties": {
|
|
168
|
+
"adjectives": {
|
|
169
|
+
"type": "array",
|
|
170
|
+
"minItems": 1,
|
|
171
|
+
"items": {"type": "string", "minLength": 1}
|
|
172
|
+
},
|
|
173
|
+
"forbidden_moves": {
|
|
174
|
+
"type": "array",
|
|
175
|
+
"minItems": 1,
|
|
176
|
+
"items": {"type": "string", "minLength": 1}
|
|
177
|
+
},
|
|
178
|
+
"sender_intent": {"type": "string", "minLength": 1},
|
|
179
|
+
"cta": {"type": "string", "minLength": 1}
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"boundary_cases": {
|
|
183
|
+
"type": "array",
|
|
184
|
+
"minItems": 2,
|
|
185
|
+
"items": {
|
|
186
|
+
"type": "object",
|
|
187
|
+
"additionalProperties": false,
|
|
188
|
+
"required": [
|
|
189
|
+
"rule_under_test",
|
|
190
|
+
"all_other_hard_rules_held",
|
|
191
|
+
"scenario",
|
|
192
|
+
"decision",
|
|
193
|
+
"reason"
|
|
194
|
+
],
|
|
195
|
+
"properties": {
|
|
196
|
+
"rule_under_test": {"type": "string", "minLength": 1},
|
|
197
|
+
"all_other_hard_rules_held": {"const": true},
|
|
198
|
+
"scenario": {"type": "string", "minLength": 1},
|
|
199
|
+
"decision": {"enum": ["A", "B", "pass"]},
|
|
200
|
+
"reason": {"type": "string", "minLength": 1}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
"negative_playback": {
|
|
205
|
+
"type": "array",
|
|
206
|
+
"minItems": 1,
|
|
207
|
+
"items": {
|
|
208
|
+
"type": "object",
|
|
209
|
+
"additionalProperties": false,
|
|
210
|
+
"required": ["omission_or_weak_signal", "founder_response", "result"],
|
|
211
|
+
"properties": {
|
|
212
|
+
"omission_or_weak_signal": {"type": "string", "minLength": 1},
|
|
213
|
+
"founder_response": {"type": "string", "minLength": 1},
|
|
214
|
+
"result": {"type": "string", "minLength": 1}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
"research_findings": {
|
|
219
|
+
"type": "array",
|
|
220
|
+
"items": {
|
|
221
|
+
"type": "object",
|
|
222
|
+
"additionalProperties": false,
|
|
223
|
+
"required": [
|
|
224
|
+
"field",
|
|
225
|
+
"value",
|
|
226
|
+
"source",
|
|
227
|
+
"state",
|
|
228
|
+
"used_in_configuration",
|
|
229
|
+
"founder_confirmation"
|
|
230
|
+
],
|
|
231
|
+
"properties": {
|
|
232
|
+
"field": {
|
|
233
|
+
"type": "string",
|
|
234
|
+
"minLength": 1,
|
|
235
|
+
"description": "Dotted path to the real configured output field when used_in_configuration is true."
|
|
236
|
+
},
|
|
237
|
+
"value": {
|
|
238
|
+
"description": "Must deep-equal the configured field resolved from field when used_in_configuration is true."
|
|
239
|
+
},
|
|
240
|
+
"source": {"type": "string", "minLength": 1},
|
|
241
|
+
"state": {
|
|
242
|
+
"enum": ["inferred", "founder_confirmed", "founder_corrected"]
|
|
243
|
+
},
|
|
244
|
+
"used_in_configuration": {
|
|
245
|
+
"type": "boolean",
|
|
246
|
+
"description": "True only after founder confirmation or correction and exact placement at field."
|
|
247
|
+
},
|
|
248
|
+
"founder_confirmation": {"type": ["string", "null"]}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
"founder_statement_history": {
|
|
253
|
+
"type": "array",
|
|
254
|
+
"items": {
|
|
255
|
+
"type": "object",
|
|
256
|
+
"additionalProperties": false,
|
|
257
|
+
"required": ["sequence", "field", "value"],
|
|
258
|
+
"properties": {
|
|
259
|
+
"sequence": {"type": "integer", "minimum": 1},
|
|
260
|
+
"field": {"type": "string", "minLength": 1},
|
|
261
|
+
"value": {}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|