*! version 1.1.1 13jul2018 daniel klein
program trackobs
    version 11.2

	if (replay()) {
	    gettoken 0 zero : 0 , parse(":")
		syntax [ , RETURN ]
		local 0 : copy local zero
	}

	gettoken subcmd zero : 0 , parse(" :,")
	if (inlist(`"`subcmd'"', "set", "clear", "saving", "report")) {
	    trackobs_`subcmd' `zero'
		exit
	}
	else if (`"`subcmd'"' == ":") local 0 `zero'

	local I : char _dta[trackobs_counter]
	trackobs_assert_counter , counter(`I')

	nobreak {
	    forvalues i = 1/`I' {
			local trackobs_`i' : char _dta[trackobs_`i']
		}
		local N_was = c(N)
	    capture noisily break `0'
		local rc = _rc
		local N_now = c(N)
		forvalues i = 1/`I' {
		    char _dta[trackobs_`i'] `: copy local trackobs_`i''
		}
		if (((!`rc') | (`N_was' != `N_now')) & (!mi(`"`0'"'))) {
		    local ++I
		    char _dta[trackobs_`I']     `N_was' `N_now' `0'
		    char _dta[trackobs_counter]                 `I'
		}
		if ("`return'" != "") trackobs_return
	}
	exit `rc'
end

program trackobs_set
    version 11.2

	syntax [ , CLEAR ]

	if ("`clear'" != "") capture trackobs_clear

	if (`"`: char _dta[trackobs_counter]'"' != "") {
	    display as err "trackobs counter already set"
		exit 498
	}
	else char _dta[trackobs_counter] 0
end

program trackobs_clear
    version 11.2

	syntax // nothing allowed

	local I : char _dta[trackobs_counter]
	trackobs_assert_counter , counter(`I')

	nobreak {
	    forvalues i = 1/`I' {
		    char _dta[trackobs_`i'] // void
		}
		char _dta[trackobs_counter] // void
	}
end

program trackobs_saving
    version 11.2

	local 0 using `0'
	syntax using/ [ , REPLACE ]

	local I : char _dta[trackobs_counter]
	trackobs_assert_counter , counter(`I')

	forvalues i = 1/`I' {
	    local trackobs_`i' : char _dta[trackobs_`i']
	}

	preserve
	clear
	quietly {
	    set obs `I'
		generate cmdline      = ""
		char cmdline[varname] Command
		generate long N_was   = .z
		char N_was[varname]   Obs. was
		generate long N_now   = .z
		char N_now[varname]   Obs. now
		forvalues i = 1/`I' {
			gettoken N_was trackobs_`i' : trackobs_`i'
			gettoken N_now trackobs_`i' : trackobs_`i'
			local    cmdline             `trackobs_`i''
			replace  N_was   = `N_was'          in `i'
			replace  N_now   = `N_now'          in `i'
		    replace  cmdline = `"`cmdline'"'    in `i'
		}
		char _dta[trackobs] trackobs
		save `"`using'"' , `replace'
	}
	restore
end

program trackobs_report
    version 11.2

	syntax [ using/ ]

	if (mi(`"`using'"')) {
	    tempfile using
		trackobs_saving `"`using'"'
	}

	preserve
	quietly use `"`using'"' , clear
	if (`"`: char _dta[trackobs]'"' != "trackobs") {
	    display as err "file not created by trackobs"
		exit 698
	}
	else list , noobs nodotz subvarname
	restore
end

program trackobs_return , rclass
    version 11.2

	local I : char _dta[trackobs_counter]
	if (!`I') exit
	else local trackobs_`I' : char _dta[trackobs_`I']

	gettoken N_was trackobs_`I' : trackobs_`I'
	gettoken N_now trackobs_`I' : trackobs_`I'

	return scalar N_now   = `N_now'
	return scalar N_was   = `N_was'
	return local  cmdline   `trackobs_`I''
end

program trackobs_assert_counter
    version 11.2
	capture syntax , COUNTER(numlist integer max=1 >=0)
	if (_rc) {
	    display as err "trackobs counter not set"
		exit 499
	}
end
exit

1.1.1 13jul2018 minor code polish
1.1.0 13jul2018 new colon syntax for prefix
                new option clear
				new option return
				new subcommand -saving-
				new reporting routine
                preserve characteristics accross datasets
1.0.0 11jul2018 posted on Statalist