Index: trunk/ippTools/scripts/camtest.sh
===================================================================
--- trunk/ippTools/scripts/camtest.sh	(revision 16678)
+++ trunk/ippTools/scripts/camtest.sh	(revision 16687)
@@ -8,4 +8,4 @@
 camtool -pendingimfile || exit 1
 
-camtool -addprocessedexp -cam_id 1 -uri file:///cam -bg 1 -bg_stdev 2 -bg_mean_stdev 3 -sigma_ra 1 -sigma_dec 2 -zp_mean 10 -zp_stdev 2 -fwhm 42 -fwhm_range 100 -n_stars 2 -n_extended 0 -n_astrom 42 -n_cr 10000000 -path_base file:///foo || exit 1
-camtool -addprocessedexp -cam_id 2 -uri file:///cam -bg 1 -bg_stdev 2 -bg_mean_stdev 3 -sigma_ra 1 -sigma_dec 2 -zp_mean 10 -zp_stdev 2 -fwhm 42 -fwhm_range 100 -n_stars 2 -n_extended 0 -n_astrom 42 -n_cr 10000000 -path_base file:///foo || exit 1
+camtool -addprocessedexp -cam_id 1 -uri file:///cam -bg 1 -bg_stdev 2 -bg_mean_stdev 3 -sigma_ra 1 -sigma_dec 2 -zp_mean 10 -zp_stdev 2 -n_stars 2 -n_extended 0 -n_astrom 42 -n_cr 10000000 -path_base file:///foo || exit 1
+camtool -addprocessedexp -cam_id 2 -uri file:///cam -bg 1 -bg_stdev 2 -bg_mean_stdev 3 -sigma_ra 1 -sigma_dec 2 -zp_mean 10 -zp_stdev 2 -n_stars 2 -n_extended 0 -n_astrom 42 -n_cr 10000000 -path_base file:///foo || exit 1
Index: trunk/ippTools/scripts/warptest.sh
===================================================================
--- trunk/ippTools/scripts/warptest.sh	(revision 16678)
+++ trunk/ippTools/scripts/warptest.sh	(revision 16687)
@@ -5,10 +5,8 @@
 camtest.sh || exit 1
 
-warptool -definerun -mode warp -workdir file:///foo || exit 1
-
-warptool -addinputexp -warp_id 1 -cam_id 1 || exit 1
-warptool -addinputexp -warp_id 1 -cam_id 2 || exit 1
-
-warptool -updaterun -warp_id 1 -state run || exit 1
+#warptool -definerun -mode warp -workdir file:///foo || exit 1
+#warptool -addinputexp -warp_id 1 -cam_id 1 || exit 1
+#warptool -addinputexp -warp_id 1 -cam_id 2 || exit 1
+#warptool -updaterun -warp_id 1 -state run || exit 1
 
 warptool -exp -warp_id 1 || exit 1
Index: trunk/ippTools/share/Makefile.am
===================================================================
--- trunk/ippTools/share/Makefile.am	(revision 16678)
+++ trunk/ippTools/share/Makefile.am	(revision 16687)
@@ -64,8 +64,9 @@
 	regtool_revertprocessedexp.sql \
 	regtool_revertprocessedimfile.sql \
+	stacktool_find_complete_warps.sql \
 	stacktool_inputskyfile.sql \
+	stacktool_revertsumskyfile.sql \
 	stacktool_sumskyfile.sql \
 	stacktool_tosum.sql \
-	stacktool_revertsumskyfile.sql \
 	warptool_exp.sql \
 	warptool_imfile.sql \
Index: trunk/ippTools/share/stacktool_find_complete_warps.sql
===================================================================
--- trunk/ippTools/share/stacktool_find_complete_warps.sql	(revision 16687)
+++ trunk/ippTools/share/stacktool_find_complete_warps.sql	(revision 16687)
@@ -0,0 +1,20 @@
+    SELECT
+        warp_id,
+        warpSkyfile.skycell_id,
+        warpSkyfile.tess_id
+    FROM warpRun
+    JOIN warpInputExp
+        USING(warp_id)
+    JOIN warpSkyfile 
+        USING(warp_id)
+    JOIN camRun
+        USING(cam_id)
+    JOIN chipRun
+        USING(chip_id)
+    JOIN rawExp
+        USING(exp_id)
+    WHERE
+        warpRun.state = 'stop'
+    GROUP BY
+        warp_id
+
Index: trunk/ippTools/src/stacktool.c
===================================================================
--- trunk/ippTools/src/stacktool.c	(revision 16678)
+++ trunk/ippTools/src/stacktool.c	(revision 16687)
@@ -95,4 +95,189 @@
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required options
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false);
+
+    // default
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_TIME(registered, config->args, "-registered", false, false);
+
+
+    psString query = pxDataGet("stacktool_find_complete_warps.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (config->where) {
+        psString whereClause = psDBGenerateWhereConditionSQL(config->where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("stacktool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    psHash *stacks = psHashAlloc(psArrayLength(output));
+
+    // loop over the array of metadata
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+        
+        // pull out the warp_id, skycell_id, & tess_id
+        bool status;
+        psString skycell_id = psMetadataLookupStr(&status, md, "skycell_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup skycell_id");
+            psFree(output);
+            return NULL;
+        }
+
+        psString tess_id = psMetadataLookupStr(&status, md, "tess_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup tess_id");
+            psFree(output);
+            return NULL;
+        }
+
+        // group the warps by skycell_id & tess_id
+        psString key = NULL;
+        psStringAppend(&key, "%s:%s", skycell_id, tess_id);
+
+        // check to see if the hash key already exists
+        psArray *col = psHashLookup(stacks, key);
+        if (!col) {
+            // if it doesn't, create an new psArray for this stack
+            col = psArrayAllocEmpty(0);
+            psHashAdd(stacks, key, col);
+        }
+        // add this warp to the stack for this key
+        psArrayAdd(col, 0, md);
+        psFree(key);
+
+    }
+    psFree(output);
+
+    psArray *grouped = psHashToArray(stacks);
+    psFree(stacks);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    // loop over groups
+    for (long i = 0; i < psArrayLength(grouped); i++) {
+        psArray *col = grouped->data[i];
+
+        // pull the skycell_id & tess_id from the first warp in the stack
+        bool status;
+        psString skycell_id = psMetadataLookupStr(&status, col->data[0], "skycell_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup skycell_id");
+            psFree(grouped);
+            return NULL;
+        }
+
+        psString tess_id = psMetadataLookupStr(&status, col->data[0], "tess_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup tess_id");
+            psFree(grouped);
+            return NULL;
+        }
+
+        // create a new stackRun for the group
+        stackRunRow *run = stackRunRowAlloc(
+            0,          // ID
+            "run",      // state
+            workdir,
+            NULL,       // dvodb
+            registered,
+            skycell_id,
+            tess_id
+        );
+        if (!stackRunInsertObject(config->dbh, run)) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(run);
+            psFree(grouped);
+            return false;
+        }
+
+        // figure out the new stack_id
+        psS64 stack_id = psDBLastInsertID(config->dbh);
+        run->stack_id = stack_id;
+
+        if (!stackRunPrintObject(stdout, run, !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print object");
+            psFree(run);
+            psFree(grouped);
+            return false;
+        }
+        psFree(run);
+
+        // loop over this stack and add each warp to the stackRun
+        for (long j = 0; j < psArrayLength(col); j++) {
+            bool status;
+            psS64 warp_id = psMetadataLookupS64(&status, col->data[0], "warp_id");
+            if (!status) {
+                if (!psDBRollback(config->dbh)) {
+                    psError(PS_ERR_UNKNOWN, false, "database error");
+                }
+                psError(PS_ERR_UNKNOWN, false, "failed to lookup warp_id");
+                psFree(grouped);
+                return NULL;
+            }
+
+            if (!stackInputSkyfileInsert(config->dbh,
+                stack_id,
+                warp_id
+            )) {
+                if (!psDBRollback(config->dbh)) {
+                    psError(PS_ERR_UNKNOWN, false, "database error");
+                }
+                psError(PS_ERR_UNKNOWN, false, "database error");
+                psFree(grouped);
+                return false;
+            }
+
+        }
+
+    }
+
+    // point of no return
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(grouped);
+
     return true;
 }
Index: trunk/ippTools/src/stacktoolConfig.c
===================================================================
--- trunk/ippTools/src/stacktoolConfig.c	(revision 16678)
+++ trunk/ippTools/src/stacktoolConfig.c	(revision 16687)
@@ -47,4 +47,13 @@
     // -definebyquery
     psMetadata *definebyqueryArgs = psMetadataAlloc();
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-workdir", 0,
+            "define workdir (required)", NULL);
+    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-registered",  0,
+            "time detrend run was registered", now);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple",  0,
+            "use the simple output format", false);
+    // skycell
+    // tess_id
+    // warp_id
 
     // -definerun
