carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

Reviewed 56f9ecfa. All plan line references refer to that commit.

  1. P1 — Task 7, Step 3: manual purge omits settlement. docs/superpowers/plans/2026-09-26-temporary-static-sites.md:3327 adds only lockApp to handlePurgeApp. If trash wrote its marker but failed its row update, manual purge deletes the objects while PurgeAppRow silently preserves the live row (internal/console/ownership.go:472, internal/console/store.go:1491). Subsequent settlement leaves a live row without a marker untouched (plan:1811). This violates the settle-first requirement in spec:65–73. Fix: settle under the mutex, re-read the row, and validate purge prerequisites before destructive teardown. Test manual purge immediately after a failed trash row update.
  1. P1 — Task 7, Step 3: destructive handlers retain protection decisions made before acquiring the lock. Plan:3312–3331 places locks after the existing control-plane checks (internal/console/apicli.go:1209, internal/console/appsettings.go:991, internal/console/ownership.go:335). Meanwhile, Task 6 gives the control-plane setter the same mutex (plan:2859). A delete request can pass its protection check, wait while the setter marks the app protected, then acquire the lock and trash it anyway. The missing row re-read also allows a delayed trash request to recreate a marker after its row has been purged. Fix: re-check existence and control-plane protection after locking and settling. Add deterministic tests that change these preconditions while a destructive request waits.
  1. P2 — Task 8, Step 1: the ErrNotTrashed tests bypass the branch they claim to exercise. TestEarlyPurgeNotTrashedBranches removes the marker before invoking the pass (plan:3502–3525). The settle sweep repairs or removes the row first (plan:3722), so purgeOne returns at plan:3816 without reaching the recovery branch at plan:3844. Deleting that branch would leave these tests passing. Fix: use a store hook to remove the marker after initial settlement but before the purge precondition reads it. Assert that ErrNotTrashed occurs and that both recovery outcomes work.
  1. P2 — Task 6, Step 1: the allowed static-instance test accepts unrelated failures. At plan:2579, TestTemporaryInstanceCreateIsStaticAtThePlatformHost discards the HTTP status and checks only that the error lacks the keep hint. A 400 or 500 therefore passes, despite further validation before creation (internal/console/apiinstances.go:972). Fix: require the successful status and read back the instance record, checking its account, app, platform hostname and static kind.
  1. P2 — Task 11, Step 3: an explicitly empty --ttl silently selects ordinary deployment. temporaryDeployFlags detects --ttl through its nonblank value (plan:5020–5029); its caller supplies no flag-presence information (plan:5357). Consequently, carlos deploy --ttl "" ./dist follows ordinary app resolution instead of refusing an invalid TTL or creating a temporary app. This contradicts spec:442, where --ttl implies temporary creation. Fix: pass flagWasSet(fs, "ttl"), validate every explicitly supplied value, and test empty and whitespace-only values through the CLI entry point.

Verdict: revise.