I had a go at creating this contraption as described:


as a paste-able example:
%%WGT0{"w":[{"name":"q1","type":"contraption","size":[185,24],"pos":[100,84],"def":"questionAsker","widgets":{"questionText":{"value":"A hotdog is a sandwich?"},"bp":{},"bz":{},"bn":{},"answerVal":{"value":"-1"}}}],"d":{"questionAsker":{"name":"questionAsker","size":[185,24],"resizable":1,"margin":[3,3,71,4],"description":"Your choices will be recorded for quality-assurance purposes.","version":1,"script":"on get_question do\n questionText.text\nend\non set_question v do\n questionText.text:v\nend\non get_answer do\n answerVal.data\nend\non set_answer v do\n answerVal.data:v\n view[]\nend\non view do\n each val wid in (bp,bz,bn) dict 1,0,-1\n wid.show:(\"solid\",\"invert\")[answerVal.data~val]\n end\nend","attributes":{"name":["question","answer"],"label":["Question","Answer"],"type":["string","number"]},"widgets":{"questionText":{"type":"field","size":[115,20],"pos":[2,2],"locked":1,"border":0,"value":"Question?"},"bp":{"type":"button","size":[19,20],"pos":[120,2],"script":"on click do\n answerVal.data:1\n view[]\nend","text":"+","style":"rect"},"bz":{"type":"button","size":[19,20],"pos":[142,2],"script":"on click do\n answerVal.data:0\n view[]\nend","show":"invert","text":"0","style":"rect"},"bn":{"type":"button","size":[19,20],"pos":[164,2],"script":"on click do\n answerVal.data:-1\n view[]\nend","text":"-","style":"rect"},"answerVal":{"type":"field","size":[63,20],"pos":[120,-38],"show":"none","style":"plain","value":"0"}}}}}
Millie's on the right track with respect to using "me.parent.widgets". The Prototype interface has a ".widgets" attribute, so this works in the design preview, but the Contraption interface does not (otherwise there'd be no "abstraction barrier" and anything could freely fiddle with the innards of contraption instances).
I use the answerVal.data attribute for reading and writing the contents of the inner field so that the .answer attribute is exposed as a number, rather than a string. I do like Millie's suggestion to use a Slider instead; fewer possibilities for using it wrong. I also modified set_answer[] to call a contraption-level view[] function that updates the highlighting status of each button; otherwise writes to this attribute won't appear visually:
on set_answer v do answerVal.data:v view[] end on view do each val wid in (bp,bz,bn) dict 1,0,-1 wid.show:("solid","invert")[answerVal.data~val] end end
The buttons themselves now just set AnswerVal.data and invoke view[]:
on click do answerVal.data:1 view[] end
Does that help clear things up?