🛡 Admin audit — Old School Vol VI
The site admin is also a player. This page exists so nobody has to take his word for it: it is an append-only record of everything done to this game or learned from it outside normal play, visible to everyone.
Covered: Game API every call civpb.com makes to the Pitboss mod (pause, end/reopen turns, timer, kick, passwords, saves, loading a save, chat, colours, WorldBuilder/replay downloads) — everything except the routine status polling; Python shell every script sent to the game's Python console by the admin tooling, with its full text and full output — a script that would print private information is a script that must not be run; Site every change of who controls which civ (civ ↔ account, co-players, game admins) and every note added below.
Not covered: direct access to the server machine (SSH) and opening a save file on a PC. Those cannot be logged from here and remain on the admin's honour, exactly as before this page existed.
Tamper check: every entry carries a SHA-256 of its content plus the previous entry's hash. Editing or deleting an entry breaks the chain; it is recomputed over the whole table each time this page loads.
✓ chain intact over 28 entries
head: 4adcf325450bf1f7ff6f33bfd79a8bc7032555724b000aacd61e91dce52fe1aa
| # | when | who | channel | what |
|---|---|---|---|---|
| 23 | 2026-09-09 21:37:59 T15 |
shell (claude) | Site Change |
delete_log_entry — entry=Reload, turn=12, at=2026-09-08T20:55:20.978524+00:00, reason=false positive: timer reset by the turn roll / by a reopened turn, not a reload
6c85a2408ad5fa47…
|
| 22 | 2026-09-09 21:37:59 T15 |
shell (claude) | Site Change |
delete_log_entry — entry=Reload, turn=1, at=2026-09-04T07:29:33.221635+00:00, reason=false positive: timer reset by the turn roll / by a reopened turn, not a reload
fd9365b10c19ec6e…
|
| 21 | 2026-09-09 19:36:25 T15 |
Robert | Game API Own turn |
end a player's turn — player 5
→ Status of player 5 set on finished.
c0c933f589764b03…
|
| 20 | 2026-09-08 07:27:54 T11 |
Gryffon | Game API Own turn |
end a player's turn — player 4
→ Status of player 4 set on finished.
8e4e75b79b36790a…
|
| 19 | 2026-09-07 21:57:23 T10 |
Gryffon | Game API Own turn |
end a player's turn — player 4
→ Status of player 4 set on finished.
4ec1a8a0c3d55d2d…
|
| 18 | 2026-09-07 21:57:23 T10 |
Gryffon | Game API Own turn |
end a player's turn — player 4
→ Status of player 4 set on finished.
4bf30b947dc60b52…
|
| 17 | 2026-09-07 21:46:35 T10 |
Robert | Game API Own turn |
end a player's turn — player 5
→ Status of player 5 set on finished.
5ea37907abf8d0d7…
|
| 16 | 2026-09-06 19:32:29 T6 |
shell (claude) | Site Information |
audit_echo_off_test
b423e312900b27ca…
|
| 15 | 2026-09-06 11:37:54 T6 |
shell (claude) | Site Information |
audit_channel_test — channel=1546091767408107560
debf9d0a21c7b749…
|
| 14 | 2026-09-06 11:36:30 T6 |
shell (claude) | Site Change |
annotate_audit — entry=12, note=Correction applied with David's and xist10's confirmation, Yynex informed: one extra turn of income granted by the 2021 DLL's reopen bug on turn
af947d665f7589e7…
|
| 13 | 2026-09-06 11:36:30 T6 |
shell (claude) | Game API Information |
write a save file — OSV6_T6_after_double_turn_fix
→ ok
11573a4cc9007569…
|
| 12 | 2026-09-06 11:36:13 T6 |
shell (claude) | Python shell Information |
python — 160-line script, 843 chars of output
script# Reverse ONE extra CvPlayer::doTurn() that the 2021 PBMod DLL runs when a turn is
# reopened (CvNetTurnIncomplete -> setTurnActive(true) -> doTurn; fixed upstream 2023).
# Runs inside Civ4's Python 2.4 via deploy/pbshell.py and is written to the public admin
# audit. It touches ONLY the slot given below. Everything it prints before the
# ---SEALED--- line is public and contains no numbers; everything after it is
# delivered by Discord DM to the affected player and the neutral map maker only.
#
# What the extra doTurn gave (source: CvPlayer::doTurn / CvCity::doTurn):
# food surplus, production, commerce (gold + beakers), city culture, plot culture on
# the 3x3 (level 1) ring, espionage points-ever; units got fresh moves and +1 fortify
# turn (capped at 5 -> no lasting effect after 5 turns).
# The amounts come from the engine's own per-turn history, which that very doTurn wrote.
# (constants live inside run(): the shell exec's top-level names are not visible to functions)
def run():
PLAYER = 1 # in-game slot (Yynex)
TURN = 1 # the turn whose doTurn ran twice
APPLY = 1 # 0 = dry run, 1 = subtract
from CvPythonExtensions import CyGlobalContext, CyMap, CommerceTypes
gc = CyGlobalContext(); game = gc.getGame(); now = game.getGameTurn()
p = gc.getPlayer(PLAYER); team = gc.getTeam(p.getTeam()); city = p.getCapitalCity()
pub = []; sec = []; bad = []
def check(cond, what):
if cond: pub.append(" ok " + what)
else:
pub.append(" FAIL " + what); bad.append(what)
if APPLY: pub.append("MODE: APPLY")
else: pub.append("MODE: dry run (nothing changed)")
pub.append("slot %d, extra doTurn on turn %d, game is on turn %d" % (PLAYER, TURN, now))
check(city is not None and not city.isNone(), "player has a capital")
if bad: return pub, sec
# --- what the extra doTurn used: the history it wrote for TURN ---
food_g = p.getAgricultureHistory(TURN); prod = p.getIndustryHistory(TURN); econ = p.getEconomyHistory(TURN)
cult = []; esp = []
for t in range(0, now + 1):
cult.append(p.getCultureHistory(t)); esp.append(p.getEspionageHistory(t))
cr = cult[TURN + 1] - cult[TURN] # city culture per turn
er = esp[TURN + 1] - esp[TURN] # espionage points per turn
per_pop = gc.getFOOD_CONSUMPTION_PER_POPULATION()
food = food_g - per_pop # size 1 at TURN
check(food_g > 0 and econ > 0, "history for turn %d is populated (city existed at the extra doTurn)" % TURN)
check(cr == city.getCommerceRate(CommerceTypes.COMMERCE_CULTURE), "culture rate from history equals the city's current rate")
steady = 1
for t in range(TURN + 1, now):
if cult[t + 1] - cult[t] != cr: steady = 0
check(steady, "culture rate constant since turn %d" % TURN)
check(cult[now] == city.getCulture(PLAYER), "city culture equals the history (no other culture source)")
# gold / beakers split: exact if total commerce is unchanged since TURN, else from current rates
same_comm = (p.getEconomyHistory(now) == econ)
gold_rate = p.calculateGoldRate()
if same_comm:
beakers = p.calculateResearchRate(-1)
split_note = "total commerce unchanged since turn %d: gold/beaker split taken from the current rates" % TURN
else:
beakers = econ - cr - er - gold_rate
split_note = "total commerce changed since turn %d: beakers = total - culture - espionage - net gold rate" % TURN
check(beakers >= 0, "beaker amount is non-negative")
# --- current state that must absorb the subtraction ---
pop = city.getPopulation(); fbox = city.getFood(); pbox = city.getProduction()
tech = p.getCurrentResearch(); rprog = -1
if tech >= 0: rprog = team.getResearchProgress(tech)
# Where did the turn-1 food surplus go? Worker/Settler builds turn it into hammers
# (CvCity::getProductionDifference adds it, foodDifference clamps to <= 0), so the food
# box stays flat. Decide from the state, never from a guess:
# box < surplus at size 1 -> the box never accumulated -> it went into the build (growth
# at size 1 needs far more than a handful of turns can give)
# box < surplus at size 2+ -> the city grew a turn early
# box >= surplus, and a food-production build now -> could be either; refuse, ask the player
fp_now = city.isFoodProduction()
grew_early = 0; to_hammers = 0; ambiguous = 0
if fbox < food and pop == 1: to_hammers = 1
elif fbox < food: grew_early = 1
elif fp_now: ambiguous = 1
hammers = prod
if to_hammers:
# same tile and same build as on turn 1 -> the engine's own per-turn figure is exact
if fp_now and p.getIndustryHistory(now) == prod and p.getAgricultureHistory(now) == food_g:
hammers = city.getCurrentProductionDifference(False, False)
ham_note = "hammers = current production difference (same yields and build as turn %d)" % TURN
else:
hammers = (prod * (100 + city.getProductionModifier())) / 100 + food
ham_note = "hammers = base production x build modifier + food surplus"
else:
ham_note = "hammers = production yield of turn %d" % TURN
if to_hammers: food_take = 0
else: food_take = food
build_early = (pbox < hammers)
tech_early = (tech >= 0 and rprog < beakers)
check(city.getCulture(PLAYER) - cr >= game.getCultureThreshold(city.getCultureLevel()) or city.getCultureLevel() <= 1,
"borders stay as they are after removing one turn of culture")
if to_hammers: pub.append(" note the turn-%d surplus went into a Worker/Settler build -> taken off the build, food box untouched" % TURN)
elif grew_early: pub.append(" note growth happened a turn early -> will be undone: size -1, food box restored")
else: pub.append(" ok food box holds one turn of surplus (no early growth to undo)")
check(not ambiguous, "food path is unambiguous (otherwise: ask the player what was being built on turn %d)" % TURN)
check(not build_early, "current build holds one turn of hammers (no early completion)")
check(not tech_early, "current research holds one turn of beakers (no early tech)")
check(p.getGold() >= gold_rate, "treasury holds one turn of gold")
# --- plots that got the extra doPlotCulture: level-1 ring, same filter as the engine ---
plots = []; cx = city.getX(); cy = city.getY(); mp = CyMap()
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
pl = mp.plot(cx + dx, cy + dy)
if pl is not None and not pl.isNone() and pl.isPotentialCityWorkForArea(city.area()):
plots.append(pl)
plot_c = cr + 1 # ((level - range) * factor) + rate + 1 with level = range = 1
# --- sealed: the actual numbers ---
if APPLY: sec.append("APPLY RUN for slot %d (%s): the amounts below are being subtracted now." % (PLAYER, p.getName()))
else:
sec.append("DRY RUN for slot %d (%s) - nothing has been changed. These are the amounts the extra turn-%d doTurn" % (PLAYER, p.getName(), TURN))
sec.append("granted, read from the engine's own per-turn history. They will be subtracted, with this same script,")
sec.append("only after David confirms. If anything here does not match what you saw in game, say so first.")
sec.append(" (%s)" % split_note)
sec.append("One turn of income to remove:")
sec.append(" food surplus %+d (gross %d - %d eaten, box gets %+d) -> food box %d, size %d" % (-food, food_g, per_pop, -food_take, fbox, pop))
sec.append(" hammers %+d (%s) -> build progress %d / %d, food-production build now: %d" % (-hammers, ham_note, pbox, city.getProductionNeeded(), fp_now))
sec.append(" gold %+d (net gold rate) -> treasury %d" % (-gold_rate, p.getGold()))
sec.append(" beakers %+d -> research progress %d" % (-beakers, rprog))
sec.append(" city culture %+d -> city culture %d (threshold for current level %d)" % (-cr, city.getCulture(PLAYER), game.getCultureThreshold(city.getCultureLevel())))
sec.append(" plot culture %+d on each of %d tiles of the 3x3 ring" % (-plot_c, len(plots)))
sec.append(" espionage %+d points-ever (team counter only; nobody met, so no points against anyone) -> counter %d" % (-er, team.getEspionagePointsEver()))
sec.append(" total commerce that turn %d = gold %d + beakers %d + culture %d + espionage %d" % (econ, gold_rate, beakers, cr, er))
sec.append(" city size %d, gold/research slider %d/%d%%" % (pop, p.getCommercePercent(CommerceTypes.COMMERCE_GOLD), p.getCommercePercent(CommerceTypes.COMMERCE_RESEARCH)))
if to_hammers: sec.append(" NOTE food box < surplus at size 1: the turn-%d surplus was converted into hammers (Worker/Settler); nothing to take from the food box" % TURN)
if grew_early: sec.append(" NOTE food box < surplus: growth happened a turn early; fix = size -1 and food box restored")
if ambiguous: sec.append(" NOTE cannot tell whether the turn-%d surplus went to the box or the build - NOT applied, please tell David what you were building on turn %d" % (TURN, TURN))
if build_early: sec.append(" NOTE build progress < hammers: an item completed a turn early; fix = take the rest off the next item")
if tech_early: sec.append(" NOTE research progress < beakers: a tech finished a turn early; fix needs a manual decision")
sec.append(" not reversible: one turn (T%d) of working the better tile the early border pop gave" % (TURN + 4))
if APPLY and not bad:
if grew_early:
city.setPopulation(pop - 1) # governor re-picks the worked tile; the player can change it
city.setFood(fbox - food + city.growthThreshold())
elif food_take > 0:
city.changeFood(-food_take)
city.changeProduction(-hammers)
p.changeGold(-gold_rate)
if tech >= 0 and beakers > 0: team.changeResearchProgress(tech, -beakers, PLAYER)
city.setCulture(PLAYER, city.getCulture(PLAYER) - cr, False)
for pl in plots: pl.changeCulture(PLAYER, -plot_c, True)
if er > 0: team.changeEspionagePointsEver(-min(er, team.getEspionagePointsEver())) # never below zero
pub.append("APPLIED (amounts in the sealed part)")
sec.append("APPLIED -> size %d, food box %d / %d, build %d / %d, gold %d, research %d, city culture %d" % (
city.getPopulation(), city.getFood(), city.growthThreshold(), city.getProduction(), city.getProductionNeeded(),
p.getGold(), team.getResearchProgress(tech), city.getCulture(PLAYER)))
elif APPLY:
pub.append("NOT applied: %d check(s) failed" % len(bad))
return pub, sec
import traceback
try:
pub, sec = run()
except:
pub, sec = ["FAILED before producing anything:", traceback.format_exc()], []
print "\n".join(pub)
print "---SEALED---"
print "\n".join(sec)
output — public part; a sealed part went by DM to Yynex, xist10MODE: APPLY slot 1, extra doTurn on turn 1, game is on turn 6 ok player has a capital ok history for turn 1 is populated (city existed at the extra doTurn) ok culture rate from history equals the city's current rate ok culture rate constant since turn 1 ok city culture equals the history (no other culture source) ok beaker amount is non-negative ok borders stay as they are after removing one turn of culture note the turn-1 surplus went into a Worker/Settler build -> taken off the build, food box untouched ok food path is unambiguous (otherwise: ask the player what was being built on turn 1) ok current build holds one turn of hammers (no early completion) ok current research holds one turn of beakers (no early tech) ok treasury holds one turn of gold APPLIED (amounts in the sealed part) 🔒 sealed part: not stored, not shown to anyone here (admins included); sha256 7af71c5663a1597f362a9fffcbd33d299e4c2c58157bda56ddff9ef73dfc0bad
📝 Correction applied with David's and xist10's confirmation, Yynex informed: one extra turn of income granted by the 2021 DLL's reopen bug on turn 1 (Yynex reopened via civpb.com) was subtracted - hammers (incl. the food surplus that a Worker/Settler build converts), gold, beakers, city + plot culture, espionage counter. Amounts sealed (DM to Yynex and xist10). Save before: OSV6_T6_before_double_turn_fix, after: OSV6_T6_after_double_turn_fix.
9e55c107412a2aa0…
|
| 11 | 2026-09-06 11:36:04 T6 |
shell (claude) | Game API Information |
write a save file — OSV6_T6_before_double_turn_fix
→ ok
9e3833ba297c375e…
|
| 10 | 2026-09-06 11:35:52 T6 |
shell (claude) | Python shell Information |
python — 160-line script, 826 chars of output
script# Reverse ONE extra CvPlayer::doTurn() that the 2021 PBMod DLL runs when a turn is
# reopened (CvNetTurnIncomplete -> setTurnActive(true) -> doTurn; fixed upstream 2023).
# Runs inside Civ4's Python 2.4 via deploy/pbshell.py and is written to the public admin
# audit. It touches ONLY the slot given below. Everything it prints before the
# ---SEALED--- line is public and contains no numbers; everything after it is
# delivered by Discord DM to the affected player and the neutral map maker only.
#
# What the extra doTurn gave (source: CvPlayer::doTurn / CvCity::doTurn):
# food surplus, production, commerce (gold + beakers), city culture, plot culture on
# the 3x3 (level 1) ring, espionage points-ever; units got fresh moves and +1 fortify
# turn (capped at 5 -> no lasting effect after 5 turns).
# The amounts come from the engine's own per-turn history, which that very doTurn wrote.
# (constants live inside run(): the shell exec's top-level names are not visible to functions)
def run():
PLAYER = 1 # in-game slot (Yynex)
TURN = 1 # the turn whose doTurn ran twice
APPLY = 0 # 0 = dry run, 1 = subtract
from CvPythonExtensions import CyGlobalContext, CyMap, CommerceTypes
gc = CyGlobalContext(); game = gc.getGame(); now = game.getGameTurn()
p = gc.getPlayer(PLAYER); team = gc.getTeam(p.getTeam()); city = p.getCapitalCity()
pub = []; sec = []; bad = []
def check(cond, what):
if cond: pub.append(" ok " + what)
else:
pub.append(" FAIL " + what); bad.append(what)
if APPLY: pub.append("MODE: APPLY")
else: pub.append("MODE: dry run (nothing changed)")
pub.append("slot %d, extra doTurn on turn %d, game is on turn %d" % (PLAYER, TURN, now))
check(city is not None and not city.isNone(), "player has a capital")
if bad: return pub, sec
# --- what the extra doTurn used: the history it wrote for TURN ---
food_g = p.getAgricultureHistory(TURN); prod = p.getIndustryHistory(TURN); econ = p.getEconomyHistory(TURN)
cult = []; esp = []
for t in range(0, now + 1):
cult.append(p.getCultureHistory(t)); esp.append(p.getEspionageHistory(t))
cr = cult[TURN + 1] - cult[TURN] # city culture per turn
er = esp[TURN + 1] - esp[TURN] # espionage points per turn
per_pop = gc.getFOOD_CONSUMPTION_PER_POPULATION()
food = food_g - per_pop # size 1 at TURN
check(food_g > 0 and econ > 0, "history for turn %d is populated (city existed at the extra doTurn)" % TURN)
check(cr == city.getCommerceRate(CommerceTypes.COMMERCE_CULTURE), "culture rate from history equals the city's current rate")
steady = 1
for t in range(TURN + 1, now):
if cult[t + 1] - cult[t] != cr: steady = 0
check(steady, "culture rate constant since turn %d" % TURN)
check(cult[now] == city.getCulture(PLAYER), "city culture equals the history (no other culture source)")
# gold / beakers split: exact if total commerce is unchanged since TURN, else from current rates
same_comm = (p.getEconomyHistory(now) == econ)
gold_rate = p.calculateGoldRate()
if same_comm:
beakers = p.calculateResearchRate(-1)
split_note = "total commerce unchanged since turn %d: gold/beaker split taken from the current rates" % TURN
else:
beakers = econ - cr - er - gold_rate
split_note = "total commerce changed since turn %d: beakers = total - culture - espionage - net gold rate" % TURN
check(beakers >= 0, "beaker amount is non-negative")
# --- current state that must absorb the subtraction ---
pop = city.getPopulation(); fbox = city.getFood(); pbox = city.getProduction()
tech = p.getCurrentResearch(); rprog = -1
if tech >= 0: rprog = team.getResearchProgress(tech)
# Where did the turn-1 food surplus go? Worker/Settler builds turn it into hammers
# (CvCity::getProductionDifference adds it, foodDifference clamps to <= 0), so the food
# box stays flat. Decide from the state, never from a guess:
# box < surplus at size 1 -> the box never accumulated -> it went into the build (growth
# at size 1 needs far more than a handful of turns can give)
# box < surplus at size 2+ -> the city grew a turn early
# box >= surplus, and a food-production build now -> could be either; refuse, ask the player
fp_now = city.isFoodProduction()
grew_early = 0; to_hammers = 0; ambiguous = 0
if fbox < food and pop == 1: to_hammers = 1
elif fbox < food: grew_early = 1
elif fp_now: ambiguous = 1
hammers = prod
if to_hammers:
# same tile and same build as on turn 1 -> the engine's own per-turn figure is exact
if fp_now and p.getIndustryHistory(now) == prod and p.getAgricultureHistory(now) == food_g:
hammers = city.getCurrentProductionDifference(False, False)
ham_note = "hammers = current production difference (same yields and build as turn %d)" % TURN
else:
hammers = (prod * (100 + city.getProductionModifier())) / 100 + food
ham_note = "hammers = base production x build modifier + food surplus"
else:
ham_note = "hammers = production yield of turn %d" % TURN
if to_hammers: food_take = 0
else: food_take = food
build_early = (pbox < hammers)
tech_early = (tech >= 0 and rprog < beakers)
check(city.getCulture(PLAYER) - cr >= game.getCultureThreshold(city.getCultureLevel()) or city.getCultureLevel() <= 1,
"borders stay as they are after removing one turn of culture")
if to_hammers: pub.append(" note the turn-%d surplus went into a Worker/Settler build -> taken off the build, food box untouched" % TURN)
elif grew_early: pub.append(" note growth happened a turn early -> will be undone: size -1, food box restored")
else: pub.append(" ok food box holds one turn of surplus (no early growth to undo)")
check(not ambiguous, "food path is unambiguous (otherwise: ask the player what was being built on turn %d)" % TURN)
check(not build_early, "current build holds one turn of hammers (no early completion)")
check(not tech_early, "current research holds one turn of beakers (no early tech)")
check(p.getGold() >= gold_rate, "treasury holds one turn of gold")
# --- plots that got the extra doPlotCulture: level-1 ring, same filter as the engine ---
plots = []; cx = city.getX(); cy = city.getY(); mp = CyMap()
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
pl = mp.plot(cx + dx, cy + dy)
if pl is not None and not pl.isNone() and pl.isPotentialCityWorkForArea(city.area()):
plots.append(pl)
plot_c = cr + 1 # ((level - range) * factor) + rate + 1 with level = range = 1
# --- sealed: the actual numbers ---
if APPLY: sec.append("APPLY RUN for slot %d (%s): the amounts below are being subtracted now." % (PLAYER, p.getName()))
else:
sec.append("DRY RUN for slot %d (%s) - nothing has been changed. These are the amounts the extra turn-%d doTurn" % (PLAYER, p.getName(), TURN))
sec.append("granted, read from the engine's own per-turn history. They will be subtracted, with this same script,")
sec.append("only after David confirms. If anything here does not match what you saw in game, say so first.")
sec.append(" (%s)" % split_note)
sec.append("One turn of income to remove:")
sec.append(" food surplus %+d (gross %d - %d eaten, box gets %+d) -> food box %d, size %d" % (-food, food_g, per_pop, -food_take, fbox, pop))
sec.append(" hammers %+d (%s) -> build progress %d / %d, food-production build now: %d" % (-hammers, ham_note, pbox, city.getProductionNeeded(), fp_now))
sec.append(" gold %+d (net gold rate) -> treasury %d" % (-gold_rate, p.getGold()))
sec.append(" beakers %+d -> research progress %d" % (-beakers, rprog))
sec.append(" city culture %+d -> city culture %d (threshold for current level %d)" % (-cr, city.getCulture(PLAYER), game.getCultureThreshold(city.getCultureLevel())))
sec.append(" plot culture %+d on each of %d tiles of the 3x3 ring" % (-plot_c, len(plots)))
sec.append(" espionage %+d points-ever (team counter only; nobody met, so no points against anyone) -> counter %d" % (-er, team.getEspionagePointsEver()))
sec.append(" total commerce that turn %d = gold %d + beakers %d + culture %d + espionage %d" % (econ, gold_rate, beakers, cr, er))
sec.append(" city size %d, gold/research slider %d/%d%%" % (pop, p.getCommercePercent(CommerceTypes.COMMERCE_GOLD), p.getCommercePercent(CommerceTypes.COMMERCE_RESEARCH)))
if to_hammers: sec.append(" NOTE food box < surplus at size 1: the turn-%d surplus was converted into hammers (Worker/Settler); nothing to take from the food box" % TURN)
if grew_early: sec.append(" NOTE food box < surplus: growth happened a turn early; fix = size -1 and food box restored")
if ambiguous: sec.append(" NOTE cannot tell whether the turn-%d surplus went to the box or the build - NOT applied, please tell David what you were building on turn %d" % (TURN, TURN))
if build_early: sec.append(" NOTE build progress < hammers: an item completed a turn early; fix = take the rest off the next item")
if tech_early: sec.append(" NOTE research progress < beakers: a tech finished a turn early; fix needs a manual decision")
sec.append(" not reversible: one turn (T%d) of working the better tile the early border pop gave" % (TURN + 4))
if APPLY and not bad:
if grew_early:
city.setPopulation(pop - 1) # governor re-picks the worked tile; the player can change it
city.setFood(fbox - food + city.growthThreshold())
elif food_take > 0:
city.changeFood(-food_take)
city.changeProduction(-hammers)
p.changeGold(-gold_rate)
if tech >= 0 and beakers > 0: team.changeResearchProgress(tech, -beakers, PLAYER)
city.setCulture(PLAYER, city.getCulture(PLAYER) - cr, False)
for pl in plots: pl.changeCulture(PLAYER, -plot_c, True)
if er > 0: team.changeEspionagePointsEver(-min(er, team.getEspionagePointsEver())) # never below zero
pub.append("APPLIED (amounts in the sealed part)")
sec.append("APPLIED -> size %d, food box %d / %d, build %d / %d, gold %d, research %d, city culture %d" % (
city.getPopulation(), city.getFood(), city.growthThreshold(), city.getProduction(), city.getProductionNeeded(),
p.getGold(), team.getResearchProgress(tech), city.getCulture(PLAYER)))
elif APPLY:
pub.append("NOT applied: %d check(s) failed" % len(bad))
return pub, sec
import traceback
try:
pub, sec = run()
except:
pub, sec = ["FAILED before producing anything:", traceback.format_exc()], []
print "\n".join(pub)
print "---SEALED---"
print "\n".join(sec)
output — public part; a sealed part went by DM to Yynex, xist10MODE: dry run (nothing changed) slot 1, extra doTurn on turn 1, game is on turn 6 ok player has a capital ok history for turn 1 is populated (city existed at the extra doTurn) ok culture rate from history equals the city's current rate ok culture rate constant since turn 1 ok city culture equals the history (no other culture source) ok beaker amount is non-negative ok borders stay as they are after removing one turn of culture note the turn-1 surplus went into a Worker/Settler build -> taken off the build, food box untouched ok food path is unambiguous (otherwise: ask the player what was being built on turn 1) ok current build holds one turn of hammers (no early completion) ok current research holds one turn of beakers (no early tech) ok treasury holds one turn of gold 🔒 sealed part: not stored, not shown to anyone here (admins included); sha256 7abcd3991d98e537b8ade366ad42b7692278608d3f253dc558d250b8ac87adb4
5314d2d3bbf9acc3…
|
| 9 | 2026-09-06 11:29:43 T6 |
shell (claude) | Site Change |
annotate_audit — entry=6, note=Tooling test by the AI while debugging the shell wrapper; the second output line is one raw number from a player history that should have been sea
110b2eab82127c7d…
|
| 8 | 2026-09-06 11:29:00 T6 |
shell (claude) | Python shell Information |
python — 127-line script, 701 chars of output
script# Reverse ONE extra CvPlayer::doTurn() that the 2021 PBMod DLL runs when a turn is
# reopened (CvNetTurnIncomplete -> setTurnActive(true) -> doTurn; fixed upstream 2023).
# Runs inside Civ4's Python 2.4 via deploy/pbshell.py and is written to the public admin
# audit. It touches ONLY the slot given below. Everything it prints before the
# ---SEALED--- line is public and contains no numbers; everything after it is
# delivered by Discord DM to the affected player and the neutral map maker only.
#
# What the extra doTurn gave (source: CvPlayer::doTurn / CvCity::doTurn):
# food surplus, production, commerce (gold + beakers), city culture, plot culture on
# the 3x3 (level 1) ring, espionage points-ever; units got fresh moves and +1 fortify
# turn (capped at 5 -> no lasting effect after 5 turns).
# The amounts come from the engine's own per-turn history, which that very doTurn wrote.
# (constants live inside run(): the shell exec's top-level names are not visible to functions)
def run():
PLAYER = 1 # in-game slot (Yynex)
TURN = 1 # the turn whose doTurn ran twice
APPLY = 0 # 0 = dry run, 1 = subtract
from CvPythonExtensions import CyGlobalContext, CyMap, CommerceTypes
gc = CyGlobalContext(); game = gc.getGame(); now = game.getGameTurn()
p = gc.getPlayer(PLAYER); team = gc.getTeam(p.getTeam()); city = p.getCapitalCity()
pub = []; sec = []; bad = []
def check(cond, what):
if cond: pub.append(" ok " + what)
else:
pub.append(" FAIL " + what); bad.append(what)
if APPLY: pub.append("MODE: APPLY")
else: pub.append("MODE: dry run (nothing changed)")
pub.append("slot %d, extra doTurn on turn %d, game is on turn %d" % (PLAYER, TURN, now))
check(city is not None and not city.isNone(), "player has a capital")
if bad: return pub, sec
# --- what the extra doTurn used: the history it wrote for TURN ---
food_g = p.getAgricultureHistory(TURN); prod = p.getIndustryHistory(TURN); econ = p.getEconomyHistory(TURN)
cult = []; esp = []
for t in range(0, now + 1):
cult.append(p.getCultureHistory(t)); esp.append(p.getEspionageHistory(t))
cr = cult[TURN + 1] - cult[TURN] # city culture per turn
er = esp[TURN + 1] - esp[TURN] # espionage points per turn
per_pop = gc.getFOOD_CONSUMPTION_PER_POPULATION()
food = food_g - per_pop # size 1 at TURN
check(food_g > 0 and econ > 0, "history for turn %d is populated (city existed at the extra doTurn)" % TURN)
check(cr == city.getCommerceRate(CommerceTypes.COMMERCE_CULTURE), "culture rate from history equals the city's current rate")
steady = 1
for t in range(TURN + 1, now):
if cult[t + 1] - cult[t] != cr: steady = 0
check(steady, "culture rate constant since turn %d" % TURN)
check(cult[now] == city.getCulture(PLAYER), "city culture equals the history (no other culture source)")
# gold / beakers split: exact if total commerce is unchanged since TURN, else from current rates
same_comm = (p.getEconomyHistory(now) == econ)
gold_rate = p.calculateGoldRate()
if same_comm:
beakers = p.calculateResearchRate(-1)
split_note = "total commerce unchanged since turn %d: gold/beaker split taken from the current rates" % TURN
else:
beakers = econ - cr - er - gold_rate
split_note = "total commerce changed since turn %d: beakers = total - culture - espionage - net gold rate" % TURN
check(beakers >= 0, "beaker amount is non-negative")
# --- current state that must absorb the subtraction ---
pop = city.getPopulation(); fbox = city.getFood(); pbox = city.getProduction()
tech = p.getCurrentResearch(); rprog = -1
if tech >= 0: rprog = team.getResearchProgress(tech)
grew_early = (fbox < food)
build_early = (pbox < prod)
tech_early = (tech >= 0 and rprog < beakers)
check(city.getCulture(PLAYER) - cr >= game.getCultureThreshold(city.getCultureLevel()) or city.getCultureLevel() <= 1,
"borders stay as they are after removing one turn of culture")
check(not grew_early, "food box holds one turn of surplus (no early growth to undo)")
check(not build_early, "current build holds one turn of hammers (no early completion)")
check(not tech_early, "current research holds one turn of beakers (no early tech)")
check(p.getGold() >= gold_rate, "treasury holds one turn of gold")
# --- plots that got the extra doPlotCulture: level-1 ring, same filter as the engine ---
plots = []; cx = city.getX(); cy = city.getY(); mp = CyMap()
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
pl = mp.plot(cx + dx, cy + dy)
if pl is not None and not pl.isNone() and pl.isPotentialCityWorkForArea(city.area()):
plots.append(pl)
plot_c = cr + 1 # ((level - range) * factor) + rate + 1 with level = range = 1
# --- sealed: the actual numbers ---
if APPLY: sec.append("APPLY RUN for slot %d (%s): the amounts below are being subtracted now." % (PLAYER, p.getName()))
else:
sec.append("DRY RUN for slot %d (%s) - nothing has been changed. These are the amounts the extra turn-%d doTurn" % (PLAYER, p.getName(), TURN))
sec.append("granted, read from the engine's own per-turn history. They will be subtracted, with this same script,")
sec.append("only after David confirms. If anything here does not match what you saw in game, say so first.")
sec.append(" (%s)" % split_note)
sec.append("One turn of income to remove:")
sec.append(" food surplus %+d (gross %d - %d eaten) -> food box %d" % (-food, food_g, per_pop, fbox))
sec.append(" hammers %+d -> build progress %d / %d" % (-prod, pbox, city.getProductionNeeded()))
sec.append(" gold %+d (net gold rate) -> treasury %d" % (-gold_rate, p.getGold()))
sec.append(" beakers %+d -> research progress %d" % (-beakers, rprog))
sec.append(" city culture %+d -> city culture %d (threshold for current level %d)" % (-cr, city.getCulture(PLAYER), game.getCultureThreshold(city.getCultureLevel())))
sec.append(" plot culture %+d on each of %d tiles of the 3x3 ring" % (-plot_c, len(plots)))
sec.append(" espionage %+d points-ever (team counter, no effect on anyone)" % (-er))
sec.append(" total commerce that turn %d = gold %d + beakers %d + culture %d + espionage %d" % (econ, gold_rate, beakers, cr, er))
sec.append(" city size %d, gold/research slider %d/%d%%" % (pop, p.getCommercePercent(CommerceTypes.COMMERCE_GOLD), p.getCommercePercent(CommerceTypes.COMMERCE_RESEARCH)))
if grew_early: sec.append(" NOTE food box < surplus: growth happened a turn early; fix = size -1 and food box restored")
if build_early: sec.append(" NOTE build progress < hammers: an item completed a turn early; fix = take the rest off the next item")
if tech_early: sec.append(" NOTE research progress < beakers: a tech finished a turn early; fix needs a manual decision")
sec.append(" not reversible: one turn (T%d) of working the better tile the early border pop gave" % (TURN + 4))
if APPLY and not bad:
city.changeFood(-food)
city.changeProduction(-prod)
p.changeGold(-gold_rate)
if tech >= 0 and beakers > 0: team.changeResearchProgress(tech, -beakers, PLAYER)
city.setCulture(PLAYER, city.getCulture(PLAYER) - cr, False)
for pl in plots: pl.changeCulture(PLAYER, -plot_c, True)
if er > 0: team.changeEspionagePointsEver(-er)
pub.append("APPLIED (amounts in the sealed part)")
sec.append("APPLIED -> food box %d, build %d/%d, gold %d, research %d, city culture %d" % (
city.getFood(), city.getProduction(), city.getProductionNeeded(), p.getGold(),
team.getResearchProgress(tech), city.getCulture(PLAYER)))
elif APPLY:
pub.append("NOT applied: %d check(s) failed" % len(bad))
return pub, sec
import traceback
try:
pub, sec = run()
except:
pub, sec = ["FAILED before producing anything:", traceback.format_exc()], []
print "\n".join(pub)
print "---SEALED---"
print "\n".join(sec)
output — public part; a sealed part went by DM to Yynex, xist10MODE: dry run (nothing changed) slot 1, extra doTurn on turn 1, game is on turn 6 ok player has a capital ok history for turn 1 is populated (city existed at the extra doTurn) ok culture rate from history equals the city's current rate ok culture rate constant since turn 1 ok city culture equals the history (no other culture source) ok beaker amount is non-negative ok borders stay as they are after removing one turn of culture FAIL food box holds one turn of surplus (no early growth to undo) ok current build holds one turn of hammers (no early completion) ok current research holds one turn of beakers (no early tech) ok treasury holds one turn of gold 🔒 sealed part: not stored, not shown to anyone here (admins included); sha256 702e339158a9dda39be59a70b5345f0771e8acca1c766fc14da2dd44915e8d2b
6fcfbdc7a9a34f7d…
|
| 7 | 2026-09-06 11:28:47 T6 |
shell (claude) | Python shell Information |
python — 131-line script, 295 chars of output
scriptimport traceback
def _main():
# Reverse ONE extra CvPlayer::doTurn() that the 2021 PBMod DLL runs when a turn is
# reopened (CvNetTurnIncomplete -> setTurnActive(true) -> doTurn; fixed upstream 2023).
# Runs inside Civ4's Python 2.4 via deploy/pbshell.py and is written to the public admin
# audit. It touches ONLY the slot given below. Everything it prints before the
# ---SEALED--- line is public and contains no numbers; everything after it is
# delivered by Discord DM to the affected player and the neutral map maker only.
#
# What the extra doTurn gave (source: CvPlayer::doTurn / CvCity::doTurn):
# food surplus, production, commerce (gold + beakers), city culture, plot culture on
# the 3x3 (level 1) ring, espionage points-ever; units got fresh moves and +1 fortify
# turn (capped at 5 -> no lasting effect after 5 turns).
# The amounts come from the engine's own per-turn history, which that very doTurn wrote.
# (constants live inside run(): the shell exec's top-level names are not visible to functions)
def run():
PLAYER = 1 # in-game slot (Yynex)
TURN = 1 # the turn whose doTurn ran twice
APPLY = 0 # 0 = dry run, 1 = subtract
from CvPythonExtensions import CyGlobalContext, CyMap, CommerceTypes
gc = CyGlobalContext(); game = gc.getGame(); now = game.getGameTurn()
p = gc.getPlayer(PLAYER); team = gc.getTeam(p.getTeam()); city = p.getCapitalCity()
pub = []; sec = []; bad = []
def check(cond, what):
if cond: pub.append(" ok " + what)
else:
pub.append(" FAIL " + what); bad.append(what)
if APPLY: pub.append("MODE: APPLY")
else: pub.append("MODE: dry run (nothing changed)")
pub.append("slot %d, extra doTurn on turn %d, game is on turn %d" % (PLAYER, TURN, now))
check(city is not None and not city.isNone(), "player has a capital")
if bad: return pub, sec
# --- what the extra doTurn used: the history it wrote for TURN ---
food_g = p.getAgricultureHistory(TURN); prod = p.getIndustryHistory(TURN); econ = p.getEconomyHistory(TURN)
cult = []; esp = []
for t in range(0, now + 1):
cult.append(p.getCultureHistory(t)); esp.append(p.getEspionageHistory(t))
cr = cult[TURN + 1] - cult[TURN] # city culture per turn
er = esp[TURN + 1] - esp[TURN] # espionage points per turn
per_pop = gc.getFOOD_CONSUMPTION_PER_POPULATION()
food = food_g - per_pop # size 1 at TURN
check(food_g > 0 and econ > 0, "history for turn %d is populated (city existed at the extra doTurn)" % TURN)
check(cr == city.getCommerceRate(CommerceTypes.COMMERCE_CULTURE), "culture rate from history equals the city's current rate")
steady = 1
for t in range(TURN + 1, now):
if cult[t + 1] - cult[t] != cr: steady = 0
check(steady, "culture rate constant since turn %d" % TURN)
check(cult[now] == city.getCulture(), "city culture equals the history (no other culture source)")
# gold / beakers split: exact if total commerce is unchanged since TURN, else from current rates
same_comm = (p.getEconomyHistory(now) == econ)
gold_rate = p.calculateGoldRate()
if same_comm:
beakers = p.calculateResearchRate(-1)
split_note = "total commerce unchanged since turn %d: gold/beaker split taken from the current rates" % TURN
else:
beakers = econ - cr - er - gold_rate
split_note = "total commerce changed since turn %d: beakers = total - culture - espionage - net gold rate" % TURN
check(beakers >= 0, "beaker amount is non-negative")
# --- current state that must absorb the subtraction ---
pop = city.getPopulation(); fbox = city.getFood(); pbox = city.getProduction()
tech = p.getCurrentResearch(); rprog = -1
if tech >= 0: rprog = team.getResearchProgress(tech)
grew_early = (fbox < food)
build_early = (pbox < prod)
tech_early = (tech >= 0 and rprog < beakers)
check(city.getCulture() - cr >= game.getCultureThreshold(city.getCultureLevel()) or city.getCultureLevel() <= 1,
"borders stay as they are after removing one turn of culture")
check(not grew_early, "food box holds one turn of surplus (no early growth to undo)")
check(not build_early, "current build holds one turn of hammers (no early completion)")
check(not tech_early, "current research holds one turn of beakers (no early tech)")
check(p.getGold() >= gold_rate, "treasury holds one turn of gold")
# --- plots that got the extra doPlotCulture: level-1 ring, same filter as the engine ---
plots = []; cx = city.getX(); cy = city.getY(); mp = CyMap()
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
pl = mp.plot(cx + dx, cy + dy)
if pl is not None and not pl.isNone() and pl.isPotentialCityWorkForArea(city.area()):
plots.append(pl)
plot_c = cr + 1 # ((level - range) * factor) + rate + 1 with level = range = 1
# --- sealed: the actual numbers ---
if APPLY: sec.append("APPLY RUN for slot %d (%s): the amounts below are being subtracted now." % (PLAYER, p.getName()))
else:
sec.append("DRY RUN for slot %d (%s) - nothing has been changed. These are the amounts the extra turn-%d doTurn" % (PLAYER, p.getName(), TURN))
sec.append("granted, read from the engine's own per-turn history. They will be subtracted, with this same script,")
sec.append("only after David confirms. If anything here does not match what you saw in game, say so first.")
sec.append(" (%s)" % split_note)
sec.append("One turn of income to remove:")
sec.append(" food surplus %+d (gross %d - %d eaten) -> food box %d" % (-food, food_g, per_pop, fbox))
sec.append(" hammers %+d -> build progress %d / %d" % (-prod, pbox, city.getProductionNeeded()))
sec.append(" gold %+d (net gold rate) -> treasury %d" % (-gold_rate, p.getGold()))
sec.append(" beakers %+d -> research progress %d" % (-beakers, rprog))
sec.append(" city culture %+d -> city culture %d (threshold for current level %d)" % (-cr, city.getCulture(), game.getCultureThreshold(city.getCultureLevel())))
sec.append(" plot culture %+d on each of %d tiles of the 3x3 ring" % (-plot_c, len(plots)))
sec.append(" espionage %+d points-ever (team counter, no effect on anyone)" % (-er))
sec.append(" total commerce that turn %d = gold %d + beakers %d + culture %d + espionage %d" % (econ, gold_rate, beakers, cr, er))
sec.append(" city size %d, gold/research slider %d/%d%%" % (pop, p.getCommercePercent(CommerceTypes.COMMERCE_GOLD), p.getCommercePercent(CommerceTypes.COMMERCE_RESEARCH)))
if grew_early: sec.append(" NOTE food box < surplus: growth happened a turn early; fix = size -1 and food box restored")
if build_early: sec.append(" NOTE build progress < hammers: an item completed a turn early; fix = take the rest off the next item")
if tech_early: sec.append(" NOTE research progress < beakers: a tech finished a turn early; fix needs a manual decision")
sec.append(" not reversible: one turn (T%d) of working the better tile the early border pop gave" % (TURN + 4))
if APPLY and not bad:
city.changeFood(-food)
city.changeProduction(-prod)
p.changeGold(-gold_rate)
if tech >= 0 and beakers > 0: team.changeResearchProgress(tech, -beakers, PLAYER)
city.setCulture(PLAYER, city.getCulture() - cr, False)
for pl in plots: pl.changeCulture(PLAYER, -plot_c, True)
if er > 0: team.changeEspionagePointsEver(-er)
pub.append("APPLIED (amounts in the sealed part)")
sec.append("APPLIED -> food box %d, build %d/%d, gold %d, research %d, city culture %d" % (
city.getFood(), city.getProduction(), city.getProductionNeeded(), p.getGold(),
team.getResearchProgress(tech), city.getCulture()))
elif APPLY:
pub.append("NOT applied: %d check(s) failed" % len(bad))
return pub, sec
pub, sec = run()
print "\n".join(pub)
print "---SEALED---"
print "\n".join(sec)
try:
_main()
except:
print "TRACEBACK"
print traceback.format_exc()
outputTRACEBACK
Traceback (most recent call last):
File "<string>", line 128, in ?
File "<string>", line 122, in _main
File "<string>", line 49, in run
ArgumentError: Python argument types in
CyCity.getCulture(CyCity)
did not match C++ signature:
getCulture(class CyCity {lvalue}, int)
dcf8959d77ba3bdc…
|
| 6 | 2026-09-06 11:28:13 T6 |
shell (claude) | Python shell Information |
python — 6-line script, 14 chars of output
scriptdef f():
from CvPythonExtensions import CyGlobalContext
gc = CyGlobalContext(); p = gc.getPlayer(1)
return ["turn %d" % gc.getGame().getGameTurn(), "hist %d" % p.getAgricultureHistory(1)]
x = f()
print "\n".join(x)
outputturn 6 hist 4 📝 Tooling test by the AI while debugging the shell wrapper; the second output line is one raw number from a player history that should have been sealed. Mistake, noted here rather than hidden.
3a2a6978ecbc7a97…
|
| 5 | 2026-09-06 11:28:11 T6 |
shell (claude) | Python shell Information |
python — 5-line script, 15 chars of output
scriptdef f():
return ["a","b"]
x = f()
print "\n".join(x)
print "---MARK---"
outputa b ---MARK--- 340d1914f1e08fac…
|
| 4 | 2026-09-06 11:27:56 T6 |
shell (claude) | Python shell Information |
python — 123-line script, 2 chars of output
script# Reverse ONE extra CvPlayer::doTurn() that the 2021 PBMod DLL runs when a turn is
# reopened (CvNetTurnIncomplete -> setTurnActive(true) -> doTurn; fixed upstream 2023).
# Runs inside Civ4's Python 2.4 via deploy/pbshell.py and is written to the public admin
# audit. It touches ONLY the slot given below. Everything it prints before the
# ---SEALED--- line is public and contains no numbers; everything after it is
# delivered by Discord DM to the affected player and the neutral map maker only.
#
# What the extra doTurn gave (source: CvPlayer::doTurn / CvCity::doTurn):
# food surplus, production, commerce (gold + beakers), city culture, plot culture on
# the 3x3 (level 1) ring, espionage points-ever; units got fresh moves and +1 fortify
# turn (capped at 5 -> no lasting effect after 5 turns).
# The amounts come from the engine's own per-turn history, which that very doTurn wrote.
# (constants live inside run(): the shell exec's top-level names are not visible to functions)
def run():
PLAYER = 1 # in-game slot (Yynex)
TURN = 1 # the turn whose doTurn ran twice
APPLY = 0 # 0 = dry run, 1 = subtract
from CvPythonExtensions import CyGlobalContext, CyMap, CommerceTypes
gc = CyGlobalContext(); game = gc.getGame(); now = game.getGameTurn()
p = gc.getPlayer(PLAYER); team = gc.getTeam(p.getTeam()); city = p.getCapitalCity()
pub = []; sec = []; bad = []
def check(cond, what):
if cond: pub.append(" ok " + what)
else:
pub.append(" FAIL " + what); bad.append(what)
if APPLY: pub.append("MODE: APPLY")
else: pub.append("MODE: dry run (nothing changed)")
pub.append("slot %d, extra doTurn on turn %d, game is on turn %d" % (PLAYER, TURN, now))
check(city is not None and not city.isNone(), "player has a capital")
if bad: return pub, sec
# --- what the extra doTurn used: the history it wrote for TURN ---
food_g = p.getAgricultureHistory(TURN); prod = p.getIndustryHistory(TURN); econ = p.getEconomyHistory(TURN)
cult = []; esp = []
for t in range(0, now + 1):
cult.append(p.getCultureHistory(t)); esp.append(p.getEspionageHistory(t))
cr = cult[TURN + 1] - cult[TURN] # city culture per turn
er = esp[TURN + 1] - esp[TURN] # espionage points per turn
per_pop = gc.getFOOD_CONSUMPTION_PER_POPULATION()
food = food_g - per_pop # size 1 at TURN
check(food_g > 0 and econ > 0, "history for turn %d is populated (city existed at the extra doTurn)" % TURN)
check(cr == city.getCommerceRate(CommerceTypes.COMMERCE_CULTURE), "culture rate from history equals the city's current rate")
steady = 1
for t in range(TURN + 1, now):
if cult[t + 1] - cult[t] != cr: steady = 0
check(steady, "culture rate constant since turn %d" % TURN)
check(cult[now] == city.getCulture(), "city culture equals the history (no other culture source)")
# gold / beakers split: exact if total commerce is unchanged since TURN, else from current rates
same_comm = (p.getEconomyHistory(now) == econ)
gold_rate = p.calculateGoldRate()
if same_comm:
beakers = p.calculateResearchRate(-1)
split_note = "total commerce unchanged since turn %d: gold/beaker split taken from the current rates" % TURN
else:
beakers = econ - cr - er - gold_rate
split_note = "total commerce changed since turn %d: beakers = total - culture - espionage - net gold rate" % TURN
check(beakers >= 0, "beaker amount is non-negative")
# --- current state that must absorb the subtraction ---
pop = city.getPopulation(); fbox = city.getFood(); pbox = city.getProduction()
tech = p.getCurrentResearch(); rprog = -1
if tech >= 0: rprog = team.getResearchProgress(tech)
grew_early = (fbox < food)
build_early = (pbox < prod)
tech_early = (tech >= 0 and rprog < beakers)
check(city.getCulture() - cr >= game.getCultureThreshold(city.getCultureLevel()) or city.getCultureLevel() <= 1,
"borders stay as they are after removing one turn of culture")
check(not grew_early, "food box holds one turn of surplus (no early growth to undo)")
check(not build_early, "current build holds one turn of hammers (no early completion)")
check(not tech_early, "current research holds one turn of beakers (no early tech)")
check(p.getGold() >= gold_rate, "treasury holds one turn of gold")
# --- plots that got the extra doPlotCulture: level-1 ring, same filter as the engine ---
plots = []; cx = city.getX(); cy = city.getY(); mp = CyMap()
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
pl = mp.plot(cx + dx, cy + dy)
if pl is not None and not pl.isNone() and pl.isPotentialCityWorkForArea(city.area()):
plots.append(pl)
plot_c = cr + 1 # ((level - range) * factor) + rate + 1 with level = range = 1
# --- sealed: the actual numbers ---
if APPLY: sec.append("APPLY RUN for slot %d (%s): the amounts below are being subtracted now." % (PLAYER, p.getName()))
else:
sec.append("DRY RUN for slot %d (%s) - nothing has been changed. These are the amounts the extra turn-%d doTurn" % (PLAYER, p.getName(), TURN))
sec.append("granted, read from the engine's own per-turn history. They will be subtracted, with this same script,")
sec.append("only after David confirms. If anything here does not match what you saw in game, say so first.")
sec.append(" (%s)" % split_note)
sec.append("One turn of income to remove:")
sec.append(" food surplus %+d (gross %d - %d eaten) -> food box %d" % (-food, food_g, per_pop, fbox))
sec.append(" hammers %+d -> build progress %d / %d" % (-prod, pbox, city.getProductionNeeded()))
sec.append(" gold %+d (net gold rate) -> treasury %d" % (-gold_rate, p.getGold()))
sec.append(" beakers %+d -> research progress %d" % (-beakers, rprog))
sec.append(" city culture %+d -> city culture %d (threshold for current level %d)" % (-cr, city.getCulture(), game.getCultureThreshold(city.getCultureLevel())))
sec.append(" plot culture %+d on each of %d tiles of the 3x3 ring" % (-plot_c, len(plots)))
sec.append(" espionage %+d points-ever (team counter, no effect on anyone)" % (-er))
sec.append(" total commerce that turn %d = gold %d + beakers %d + culture %d + espionage %d" % (econ, gold_rate, beakers, cr, er))
sec.append(" city size %d, gold/research slider %d/%d%%" % (pop, p.getCommercePercent(CommerceTypes.COMMERCE_GOLD), p.getCommercePercent(CommerceTypes.COMMERCE_RESEARCH)))
if grew_early: sec.append(" NOTE food box < surplus: growth happened a turn early; fix = size -1 and food box restored")
if build_early: sec.append(" NOTE build progress < hammers: an item completed a turn early; fix = take the rest off the next item")
if tech_early: sec.append(" NOTE research progress < beakers: a tech finished a turn early; fix needs a manual decision")
sec.append(" not reversible: one turn (T%d) of working the better tile the early border pop gave" % (TURN + 4))
if APPLY and not bad:
city.changeFood(-food)
city.changeProduction(-prod)
p.changeGold(-gold_rate)
if tech >= 0 and beakers > 0: team.changeResearchProgress(tech, -beakers, PLAYER)
city.setCulture(PLAYER, city.getCulture() - cr, False)
for pl in plots: pl.changeCulture(PLAYER, -plot_c, True)
if er > 0: team.changeEspionagePointsEver(-er)
pub.append("APPLIED (amounts in the sealed part)")
sec.append("APPLIED -> food box %d, build %d/%d, gold %d, research %d, city culture %d" % (
city.getFood(), city.getProduction(), city.getProductionNeeded(), p.getGold(),
team.getResearchProgress(tech), city.getCulture()))
elif APPLY:
pub.append("NOT applied: %d check(s) failed" % len(bad))
return pub, sec
pub, sec = run()
print "\n".join(pub)
print "---SEALED---"
print "\n".join(sec)
output428356f83e56b7ca…
|
| 3 | 2026-09-06 11:27:52 T6 |
shell (claude) | Python shell Information |
python — 3-line script, 2 chars of output
scriptdef f():
return 1+1
print f()
output2 ebc8c9cb8b041655…
|
| 2 | 2026-09-06 11:24:58 T6 |
shell (claude) | Python shell Information |
python — 123-line script, 2 chars of output
script# Reverse ONE extra CvPlayer::doTurn() that the 2021 PBMod DLL runs when a turn is
# reopened (CvNetTurnIncomplete -> setTurnActive(true) -> doTurn; fixed upstream 2023).
# Runs inside Civ4's Python 2.4 via deploy/pbshell.py and is written to the public admin
# audit. It touches ONLY the slot given below. Everything it prints before the
# ---SEALED--- line is public and contains no numbers; everything after it is
# delivered by Discord DM to the affected player and the neutral map maker only.
#
# What the extra doTurn gave (source: CvPlayer::doTurn / CvCity::doTurn):
# food surplus, production, commerce (gold + beakers), city culture, plot culture on
# the 3x3 (level 1) ring, espionage points-ever; units got fresh moves and +1 fortify
# turn (capped at 5 -> no lasting effect after 5 turns).
# The amounts come from the engine's own per-turn history, which that very doTurn wrote.
PLAYER = 1 # in-game slot (Yynex)
TURN = 1 # the turn whose doTurn ran twice
APPLY = 0 # 0 = dry run, 1 = subtract
def run():
from CvPythonExtensions import CyGlobalContext, CyMap, CommerceTypes
gc = CyGlobalContext(); game = gc.getGame(); now = game.getGameTurn()
p = gc.getPlayer(PLAYER); team = gc.getTeam(p.getTeam()); city = p.getCapitalCity()
pub = []; sec = []; bad = []
def check(cond, what):
if cond: pub.append(" ok " + what)
else:
pub.append(" FAIL " + what); bad.append(what)
if APPLY: pub.append("MODE: APPLY")
else: pub.append("MODE: dry run (nothing changed)")
pub.append("slot %d, extra doTurn on turn %d, game is on turn %d" % (PLAYER, TURN, now))
check(city is not None and not city.isNone(), "player has a capital")
if bad: return pub, sec
# --- what the extra doTurn used: the history it wrote for TURN ---
food_g = p.getAgricultureHistory(TURN); prod = p.getIndustryHistory(TURN); econ = p.getEconomyHistory(TURN)
cult = []; esp = []
for t in range(0, now + 1):
cult.append(p.getCultureHistory(t)); esp.append(p.getEspionageHistory(t))
cr = cult[TURN + 1] - cult[TURN] # city culture per turn
er = esp[TURN + 1] - esp[TURN] # espionage points per turn
per_pop = gc.getFOOD_CONSUMPTION_PER_POPULATION()
food = food_g - per_pop # size 1 at TURN
check(food_g > 0 and econ > 0, "history for turn %d is populated (city existed at the extra doTurn)" % TURN)
check(cr == city.getCommerceRate(CommerceTypes.COMMERCE_CULTURE), "culture rate from history equals the city's current rate")
steady = 1
for t in range(TURN + 1, now):
if cult[t + 1] - cult[t] != cr: steady = 0
check(steady, "culture rate constant since turn %d" % TURN)
check(cult[now] == city.getCulture(), "city culture equals the history (no other culture source)")
# gold / beakers split: exact if total commerce is unchanged since TURN, else from current rates
same_comm = (p.getEconomyHistory(now) == econ)
gold_rate = p.calculateGoldRate()
if same_comm:
beakers = p.calculateResearchRate(-1)
split_note = "total commerce unchanged since turn %d: gold/beaker split taken from the current rates" % TURN
else:
beakers = econ - cr - er - gold_rate
split_note = "total commerce changed since turn %d: beakers = total - culture - espionage - net gold rate" % TURN
check(beakers >= 0, "beaker amount is non-negative")
# --- current state that must absorb the subtraction ---
pop = city.getPopulation(); fbox = city.getFood(); pbox = city.getProduction()
tech = p.getCurrentResearch(); rprog = -1
if tech >= 0: rprog = team.getResearchProgress(tech)
grew_early = (fbox < food)
build_early = (pbox < prod)
tech_early = (tech >= 0 and rprog < beakers)
check(city.getCulture() - cr >= game.getCultureThreshold(city.getCultureLevel()) or city.getCultureLevel() <= 1,
"borders stay as they are after removing one turn of culture")
check(not grew_early, "food box holds one turn of surplus (no early growth to undo)")
check(not build_early, "current build holds one turn of hammers (no early completion)")
check(not tech_early, "current research holds one turn of beakers (no early tech)")
check(p.getGold() >= gold_rate, "treasury holds one turn of gold")
# --- plots that got the extra doPlotCulture: level-1 ring, same filter as the engine ---
plots = []; cx = city.getX(); cy = city.getY(); mp = CyMap()
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
pl = mp.plot(cx + dx, cy + dy)
if pl is not None and not pl.isNone() and pl.isPotentialCityWorkForArea(city.area()):
plots.append(pl)
plot_c = cr + 1 # ((level - range) * factor) + rate + 1 with level = range = 1
# --- sealed: the actual numbers ---
if APPLY: sec.append("APPLY RUN for slot %d (%s): the amounts below are being subtracted now." % (PLAYER, p.getName()))
else:
sec.append("DRY RUN for slot %d (%s) - nothing has been changed. These are the amounts the extra turn-%d doTurn" % (PLAYER, p.getName(), TURN))
sec.append("granted, read from the engine's own per-turn history. They will be subtracted, with this same script,")
sec.append("only after David confirms. If anything here does not match what you saw in game, say so first.")
sec.append(" (%s)" % split_note)
sec.append("One turn of income to remove:")
sec.append(" food surplus %+d (gross %d - %d eaten) -> food box %d" % (-food, food_g, per_pop, fbox))
sec.append(" hammers %+d -> build progress %d / %d" % (-prod, pbox, city.getProductionNeeded()))
sec.append(" gold %+d (net gold rate) -> treasury %d" % (-gold_rate, p.getGold()))
sec.append(" beakers %+d -> research progress %d" % (-beakers, rprog))
sec.append(" city culture %+d -> city culture %d (threshold for current level %d)" % (-cr, city.getCulture(), game.getCultureThreshold(city.getCultureLevel())))
sec.append(" plot culture %+d on each of %d tiles of the 3x3 ring" % (-plot_c, len(plots)))
sec.append(" espionage %+d points-ever (team counter, no effect on anyone)" % (-er))
sec.append(" total commerce that turn %d = gold %d + beakers %d + culture %d + espionage %d" % (econ, gold_rate, beakers, cr, er))
sec.append(" city size %d, gold/research slider %d/%d%%" % (pop, p.getCommercePercent(CommerceTypes.COMMERCE_GOLD), p.getCommercePercent(CommerceTypes.COMMERCE_RESEARCH)))
if grew_early: sec.append(" NOTE food box < surplus: growth happened a turn early; fix = size -1 and food box restored")
if build_early: sec.append(" NOTE build progress < hammers: an item completed a turn early; fix = take the rest off the next item")
if tech_early: sec.append(" NOTE research progress < beakers: a tech finished a turn early; fix needs a manual decision")
sec.append(" not reversible: one turn (T%d) of working the better tile the early border pop gave" % (TURN + 4))
if APPLY and not bad:
city.changeFood(-food)
city.changeProduction(-prod)
p.changeGold(-gold_rate)
if tech >= 0 and beakers > 0: team.changeResearchProgress(tech, -beakers, PLAYER)
city.setCulture(PLAYER, city.getCulture() - cr, False)
for pl in plots: pl.changeCulture(PLAYER, -plot_c, True)
if er > 0: team.changeEspionagePointsEver(-er)
pub.append("APPLIED (amounts in the sealed part)")
sec.append("APPLIED -> food box %d, build %d/%d, gold %d, research %d, city culture %d" % (
city.getFood(), city.getProduction(), city.getProductionNeeded(), p.getGold(),
team.getResearchProgress(tech), city.getCulture()))
elif APPLY:
pub.append("NOT applied: %d check(s) failed" % len(bad))
return pub, sec
pub, sec = run()
print "\n".join(pub)
print "---SEALED---"
print "\n".join(sec)
outputf9f9782e5d88ba7a…
|
| 1 | 2026-09-06 10:44:20 T6 |
shell (claude) | Python shell Information |
python — 2-line script, 30 chars of output
scriptfrom CvPythonExtensions import CyGlobalContext print "audit smoke test: game turn %d" % CyGlobalContext().getGame().getGameTurn() outputaudit smoke test: game turn 6 f769f76df9508a40…
|
