Index: /Dev/trunk/package.json
===================================================================
--- /Dev/trunk/package.json	(revision 478)
+++ /Dev/trunk/package.json	(revision 479)
@@ -16,5 +16,8 @@
     "grunt-coffeelint": "~0.0.6",
     "grunt-banner": "~0.1.4",
-    "grunt-amd-check": "~0.5.2"
-  }
+    "grunt-amd-check": "~0.5.2",
+    "mocha": "~1.14.0",
+    "should": "~2.1.0"
+  },
+  "dependencies": {}
 }
Index: /Dev/trunk/src/.jshintrc-generated
===================================================================
--- /Dev/trunk/src/.jshintrc-generated	(revision 478)
+++ /Dev/trunk/src/.jshintrc-generated	(revision 479)
@@ -1,5 +1,5 @@
 {
     "bitwise": true,
-    "curly": true,
+    "curly": false,
     "eqeqeq": true,
     "forin": true,
@@ -7,5 +7,5 @@
     "indent": 2,
     "latedef": true,
-    "newcap": true,
+    "newcap": false,
     "noarg": true,
     "noempty": true,
@@ -16,4 +16,5 @@
 
     "loopfunc": true,
+    "shadow": true,
 
     "devel": true,
Index: /Dev/trunk/src/Procfile
===================================================================
--- /Dev/trunk/src/Procfile	(revision 478)
+++ /Dev/trunk/src/Procfile	(revision 479)
@@ -1,1 +1,1 @@
-web: node server/heroku.js
+web: node server/bin/heroku.js
Index: /Dev/trunk/src/package.json
===================================================================
--- /Dev/trunk/src/package.json	(revision 478)
+++ /Dev/trunk/src/package.json	(revision 479)
@@ -10,5 +10,6 @@
     "q": "~0.9.6",
     "request": "~2.21.0",
-    "ya-csv": "~0.9.2"
+    "ya-csv": "~0.9.2",
+    "tv4": "~1.0.11"
   },
   "engines": {
Index: /Dev/trunk/src/server/bin/check-db.js
===================================================================
--- /Dev/trunk/src/server/bin/check-db.js	(revision 479)
+++ /Dev/trunk/src/server/bin/check-db.js	(revision 479)
@@ -0,0 +1,8 @@
+var env = require('../env')
+  , checkCouch = require('../config/check-couchdb');
+checkCouch(env.couchDbURL)
+.then(function(res){
+    console.log("done",res);
+}, function(err){
+    console.error("fail",err);
+});
Index: /Dev/trunk/src/server/bin/config-db.js
===================================================================
--- /Dev/trunk/src/server/bin/config-db.js	(revision 479)
+++ /Dev/trunk/src/server/bin/config-db.js	(revision 479)
@@ -0,0 +1,8 @@
+var env = require('../env')
+  , checkCouch = require('../config/config-couchdb');
+checkCouch(env.couchServerURL)
+.then(function(res){
+    console.log("done",res);
+}, function(err){
+    console.error("fail",err);
+});
Index: /Dev/trunk/src/server/bin/heroku.js
===================================================================
--- /Dev/trunk/src/server/bin/heroku.js	(revision 479)
+++ /Dev/trunk/src/server/bin/heroku.js	(revision 479)
@@ -0,0 +1,17 @@
+var env = require('../env');
+var configCouch = require('../config/config-couchdb');
+
+console.log("Running on",env.couchServerURL);
+
+configCouch(env.couchServerURL)
+.then(function(){
+    var app = require('../app').App({
+        couchDbURL: env.couchDbURL
+    });
+
+    app.listen(env.port, function() {
+        console.log('Listening on port',env.port);
+    });
+}, function(error){
+    console.error(error);
+});
Index: /Dev/trunk/src/server/config/check-couchdb.js
===================================================================
--- /Dev/trunk/src/server/config/check-couchdb.js	(revision 479)
+++ /Dev/trunk/src/server/config/check-couchdb.js	(revision 479)
@@ -0,0 +1,30 @@
+var Q = require('q')
+  , _ = require('underscore')
+  , tv4 = require('tv4')
+  , CouchDB = require('../util/couch').CouchDB
+  ;
+
+var schema = require('./couchdb-schema');
+
+module.exports = function(couchDbURL) {
+    var server = new CouchDB(couchDbURL);
+    var designRe = /^_design\//;
+    return server.get('/_all_docs')
+    .then(function(allDocs){
+        return _.chain(allDocs.rows)
+        .filter(function(doc){
+            return doc.id.match(designRe) === null;
+        })
+        .reduce(function(memo, doc){
+            return memo.then(function(result){
+                return server.get(doc.id)
+                .then(function(doc){
+                    var valid = tv4.validateResult(doc,schema);
+                    result[doc._id] = valid;
+                    return result;
+                });
+            });
+        }, Q.resolve({}))
+        .value();
+    }).all();
+};
Index: /Dev/trunk/src/server/config/config-couchdb.js
===================================================================
--- /Dev/trunk/src/server/config/config-couchdb.js	(revision 478)
+++ /Dev/trunk/src/server/config/config-couchdb.js	(revision 479)
@@ -1,12 +1,12 @@
 var Q = require('q')
-  , request = require('../util/q-request')
   , _ = require('underscore')
   , CouchDB = require('../util/couch').CouchDB
-  , util = require('util');
+  , util = require('util')
+  ;
 
 var designDocs = require('./couchdb-design-docs');
 
-module.exports = function(couchDbURL) {
-    var server = new CouchDB(couchDbURL);
+module.exports = function(couchServerURL) {
+    var server = new CouchDB(couchServerURL);
 
     console.log("Configuring CouchDB for QED");
@@ -31,14 +31,14 @@
         console.log("Putting documents in database.");
         return _.reduce(designDocs, function(memo, doc, docUrl) {
-            var configAction = doc.__configAction || "replace";
-            delete doc.__configAction;
-            switch (configAction) {
-            case "ignore":
-                console.log(docUrl+" ignored.");
-                return memo;
-            case "update":
-                console.log(docUrl+" updating.");
-                return memo.then(function(){
-                    server.get(docUrl)
+            return memo.then(function(){
+                var configAction = doc.__configAction || "replace";
+                delete doc.__configAction;
+                switch (configAction) {
+                case "ignore":
+                    console.log(docUrl+" ignored.");
+                    return null;
+                case "update":
+                    console.log(docUrl+" updating.");
+                    return server.get(docUrl)
                     .then(function(oldDoc){
                         _.extend(oldDoc,doc);
@@ -47,9 +47,7 @@
                         return server.put(docUrl,doc);
                     });
-                });
-            case "replace":
-                console.log(docUrl+" replacing.");
-                return memo.then(function(){
-                    server.get(docUrl)
+                case "replace":
+                    console.log(docUrl+" replacing.");
+                    return server.get(docUrl)
                     .then(function(oldDoc){
                         _.extend(doc,_.pick(oldDoc,'_id','_rev'));
@@ -58,15 +56,10 @@
                         return server.put(docUrl,doc);
                     });
-                });
-            default:
-                console.warn("Unknown action",configAction);
-                return memo;
-            }
+                default:
+                    console.warn("Unknown action",configAction);
+                    return null;
+                }
+            });
         }, Q.resolve());
-    }).then(function(results){
-        console.log("Done!", results);
-    },function(err){
-        console.error("ERROR",err,err.stack);
     });
-
 };
Index: /Dev/trunk/src/server/config/couchdb-schema.json
===================================================================
--- /Dev/trunk/src/server/config/couchdb-schema.json	(revision 479)
+++ /Dev/trunk/src/server/config/couchdb-schema.json	(revision 479)
@@ -0,0 +1,116 @@
+{
+  "title": "QED Object Schema",
+  "$ref": "#/docTypes/any",
+  "docTypes": {
+    "any": {
+      "type": "object",
+      "oneOf": [
+        { "$ref": "#/docTypes/Question" },
+        { "$ref": "#/docTypes/Survey" },
+        { "$ref": "#/docTypes/SurveyRun" },
+        { "$ref": "#/docTypes/Response" }
+      ]
+    },
+    "Question": {
+      "properties": {
+        "type": { "type": "string", "pattern": "Question" },
+        "_id": { "type": "string", "optional": true },
+        "_rev": { "type": "string", "optional": true },
+        "categories": { "type": "array", "items": { "type": "string" } },
+        "code": { "type": "string" },
+        "content": { "type": "array", "items": { "$ref": "#/contentTypes/any" } },
+        "description": { "type": "string", "optional": true },
+        "publicationDate": { "type": "string", "pattern": "", "optional": true },
+        "title": { "type": "string" },
+        "topic": { "type": "string", "optional": true }
+      },
+      "additionalProperties": false
+    },
+    "Survey": {
+      "type": "object",
+      "properties": {
+        "type": { "type": "string", "pattern": "Survey" },
+        "_id": { "type": "string", "optional": true },
+        "_rev": { "type": "string", "optional": true },
+        "publicationDate": { "type": "string", "pattern": "", "optional": true },
+        "questions": { "type": "array", "items": { "$ref": "#/docTypes/Question" } },
+        "title": { "type": "string" }
+      },
+      "additionalProperties": false
+    },
+    "SurveyRun": {
+      "type": "object",
+      "properties": {
+        "type": { "type": "string", "pattern": "SurveyRun" },
+        "_id": { "type": "string", "optional": true },
+        "_rev": { "type": "string", "optional": true },
+        "description": { "type": "string" },
+        "endDate": { "type": "string", "pattern": "" },
+        "mode": { "type": "string", "enum": [ "open", "closed" ] },
+        "startDate": { "type": "string", "pattern": "" },
+        "survey": { "$ref": "#/docTypes/Survey" },
+        "title": { "type": "string" }
+      },
+      "additionalProperties": false
+    },
+    "Response": {
+      "type": "object",
+      "properties": {
+        "type": { "type": "string", "pattern": "Response" },
+        "_id": { "type": "string", "optional": true },
+        "_rev": { "type": "string", "optional": true },
+        "answers": { "type": "object" },
+        "publicationDate": { "type": "string", "pattern": "", "optional": true },
+        "surveyRunId": { "type": "string" }
+      },
+      "additionalProperties": false
+    }
+  },
+  "contentTypes": {
+    "any": {
+      "type": "object",
+      "oneOf": [
+        { "$ref": "#/contentTypes/Text" },
+        { "$ref": "#/contentTypes/StringInput" },
+        { "$ref": "#/contentTypes/ScaleInput" }
+      ]
+    },
+    "Text": {
+        "type": "object",
+        "properties": {
+            "type": { "type": "string", "pattern": "Text" },
+            "text": { "type": "string" }
+        },
+        "additionalProperties": false
+    },
+    "StringInput": {
+        "type": "object",
+        "properties": {
+            "type": { "type": "string", "pattern": "StringInput" },
+            "text": { "type": "string" }
+        },
+        "additionalProperties": false
+    },
+    "ScaleInput": {
+        "type": "object",
+        "properties": {
+            "type": { "type": "string", "pattern": "ScaleInput" },
+            "minLabel": { "type": "string", "optional": true },
+            "min": { "type": "integer" },
+            "max": { "type": "integer" },
+            "maxLabel": { "type": "string", "optional": true },
+            "naLabel": { "type": "string", "optional": true },
+            "items": { "type": "array", "items": {
+                "type": "object",
+                "properties": {
+                    "text": { "type": "string" },
+                    "minLabel": { "type": "string", "optional": true },
+                    "maxLabel": { "type": "string", "optional": true }
+                },
+                "additionalProperties": false
+            } }
+        },
+        "additionalProperties": false
+    }
+  }
+}
Index: /Dev/trunk/src/server/env.js
===================================================================
--- /Dev/trunk/src/server/env.js	(revision 479)
+++ /Dev/trunk/src/server/env.js	(revision 479)
@@ -0,0 +1,6 @@
+var couchServerURL = (process.env.CLOUDANT_URL || 'http://localhost:5984')+'/';
+module.exports = {
+    port: process.env.PORT || 5000,
+    couchServerURL: couchServerURL,
+    couchDbURL: couchServerURL+'qed',
+};
Index: v/trunk/src/server/heroku.js
===================================================================
--- /Dev/trunk/src/server/heroku.js	(revision 478)
+++ 	(revision )
@@ -1,19 +1,0 @@
-var port = process.env.PORT || 5000;
-var couchDbURL = (process.env.CLOUDANT_URL || 'http://localhost:5984')+'/';
-
-var configCouch = require('./config/config-couchdb');
-
-console.log("Running on",couchDbURL);
-
-configCouch(couchDbURL)
-.then(function(){
-    var app = require('./app').App({
-        couchDbURL: couchDbURL+'qed'
-    });
-
-    app.listen(port, function() {
-        console.log('Listening on port',port);
-    });
-}, function(error){
-    console.error(error);
-});
Index: /Dev/trunk/src/server/util/couch.coffee
===================================================================
--- /Dev/trunk/src/server/util/couch.coffee	(revision 478)
+++ /Dev/trunk/src/server/util/couch.coffee	(revision 479)
@@ -3,4 +3,6 @@
 Q = require 'q'
 
+class CouchError extends Error
+ 
 class CouchDB
     constructor: (url) ->
@@ -29,9 +31,13 @@
             'accept': 'application/json'
         body: JSON.stringify (stringifyFunctions (data || {}))
-    request(url, options)
+    req = request(url, options)
+    req.response
     .then (res) =>
-        JSON.parse res
+        req.then (res) =>
+            JSON.parse res
+        , (err) =>
+            Q.reject (JSON.parse err)
     , (err) =>
-        Q.reject (JSON.parse err)
+        Q.reject err
 
 stringifyFunctions = (value) ->
Index: /Dev/trunk/src/server/util/q-request.js
===================================================================
--- /Dev/trunk/src/server/util/q-request.js	(revision 478)
+++ /Dev/trunk/src/server/util/q-request.js	(revision 479)
@@ -6,6 +6,7 @@
 module.exports = function(url, options) {
 
-    var dfd = q.defer();
-    dfd.response = q.defer();
+    var dfd_body = q.defer();
+    var dfd_response = q.defer();
+    dfd_body.promise.response = dfd_response.promise;
     
     options = options ? _.clone(options) : {};
@@ -14,16 +15,16 @@
     request(options,function(err,res,body){
         if ( err ) {
-            dfd.response.reject(err);
+            dfd_response.reject(err);
         } else {
+            dfd_response.resolve(res);
             if ( res.statusCode >= 200 && res.statusCode < 300 ) {
-                dfd.resolve(body);
+                dfd_body.resolve(body);
             } else {
-                dfd.reject(body);
+                dfd_body.reject(body);
             }
         }
-        dfd.response.resolve(res);
     });
 
-    return dfd.promise;
+    return dfd_body.promise;
     
 };
