orchestration: wave 4 prompts; decouple guard poll from lease renewal
Guard now polls every 30s but renews every 15 min. Coupling them meant a wave that finished just after a renewal sat undetected for a full interval with the sprite pinned hot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
You are Worker F1 on the Bookshelf project (~/bookshelf). Wave 4, part 1:
|
||||
PROVE THE SYNC + COVER PIPELINE WORKS AGAINST THE REAL SERVER.
|
||||
|
||||
FIRST READ (contract — follow exactly, do not restate):
|
||||
~/bookshelf/docs/SPEC.md (see "Sync design", "PocketBase schema")
|
||||
~/bookshelf/docs/HANDOFF.md (state + hazards already paid for — read the hazards)
|
||||
|
||||
Waves 1-3 are DONE and verified: server, data layer, metadata/scanning, all six screens.
|
||||
91 unit tests pass. But EVERYTHING so far was tested against FAKES. Worker C's own
|
||||
report flagged the biggest hole:
|
||||
|
||||
"Cover pipeline is best-effort, not fully spec-complete. Download-on-create and
|
||||
multipart upload-on-sync are UNTESTED against a real PocketBase (only via fakes).
|
||||
Logic is sound but should get a real-server smoke test."
|
||||
|
||||
That is your job. This is the single most likely place a real bug is hiding.
|
||||
|
||||
## The live server
|
||||
- PocketBase v0.40.2 at http://127.0.0.1:8090 (NOT internet-exposed, by design).
|
||||
`curl -s http://127.0.0.1:8090/api/health` currently returns 200.
|
||||
- Superuser creds: `server/.dev-credentials` (gitignored). Schema setup: `server/setup-schema.sh`
|
||||
(idempotent). Create a normal app user with `server/create-user.sh`.
|
||||
- If the server is down: `sprite-env services restart pocketbase`;
|
||||
logs at `/.sprite/logs/services/pocketbase.log`.
|
||||
- Create a THROWAWAY test user for this (e.g. livetest@example.com). Do NOT invent or
|
||||
use the owner's real email addresses — they have not been provided.
|
||||
|
||||
## Your scope
|
||||
Build a real end-to-end exercise, driven from JVM code (no emulator on this box), that
|
||||
against the LIVE server proves:
|
||||
1. auth: login as the test user, token used by PbAuthInterceptor
|
||||
2. push: create bookcase -> shelf -> book locally (client-generated 15-char ids) and
|
||||
PUSH them; verify by reading them back through the REST API that the ids were
|
||||
preserved and NOT remapped
|
||||
3. pull: mutate a record server-side, pull, confirm last-write-wins on `updated`
|
||||
4. tombstones: soft-delete locally, push, confirm `deleted=true` propagates and the
|
||||
record is filtered from local queries
|
||||
5. **COVER ROUND-TRIP (the point of this task):** attach a cover image to a book,
|
||||
multipart-upload it to the `cover` file field, then fetch the stored file back
|
||||
through `/api/files/...` and assert the bytes match what was uploaded. Use a small
|
||||
locally-generated PNG — do NOT depend on openlibrary.org being reachable.
|
||||
6. cursor handling: the per-collection `updated` cursor advances and a second pull
|
||||
is a no-op (no duplicate rows).
|
||||
|
||||
## CRITICAL — do not poison the normal test suite
|
||||
This test hits a real server and MUST NOT run in the default `testDebugUnitTest`.
|
||||
Machines without PocketBase must stay green. Gate it behind a system property, e.g.
|
||||
`assumeTrue(System.getProperty("liveServer") != null)`, and provide a runner script
|
||||
`server/live-sync-test.sh` that sets it. State plainly in your report how it is invoked.
|
||||
|
||||
## Boundaries — Worker F2 is running RIGHT NOW in this repo
|
||||
- F2 owns `app/app/build.gradle.kts` (it is adding release signing), the root `README.md`,
|
||||
and all Paparazzi/screenshot code. DO NOT TOUCH THOSE. If you need a build change,
|
||||
REPORT it, do not make it.
|
||||
- You own `server/` scripts and the live-test code. Put it in its own package
|
||||
(e.g. `org.modg.bookshelf.livesync`) so it cannot collide with F2's screenshot tests.
|
||||
- You MAY fix genuine bugs you find in data.repo / data.remote / data.local — that is
|
||||
the expected outcome. Every fix must keep the existing 91 tests passing.
|
||||
|
||||
## Build/verify — use the serialized wrapper, never ./gradlew directly
|
||||
~/bookshelf/tasks/gw assembleDebug
|
||||
~/bookshelf/tasks/gw testDebugUnitTest
|
||||
|
||||
## Definition of done — actually run these, wait for them, report REAL exit codes
|
||||
1. `~/bookshelf/tasks/gw assembleDebug` exits 0.
|
||||
2. `~/bookshelf/tasks/gw testDebugUnitTest` exits 0, ZERO failures, and the live test is
|
||||
SKIPPED in that run (prove it).
|
||||
3. The live test, run explicitly against 127.0.0.1:8090, PASSES all six points above.
|
||||
4. Report the actual bugs you found. "Everything worked first try" on an untested
|
||||
pipeline is a suspicious claim — if that is genuinely the outcome, show the evidence
|
||||
(the assertions that ran, the bytes compared).
|
||||
|
||||
## Do not repeat an earlier worker's mistake
|
||||
A worker once ended its turn saying "tests are running in the background, I'll report
|
||||
when done." That is a FAILURE. Run it, WAIT, report observed exit codes. A truthfully
|
||||
reported gap is worth more than a false green.
|
||||
|
||||
## Report (short — read by a token-constrained orchestrator)
|
||||
- real exit codes; how to invoke the live test; PASS/FAIL per numbered point above
|
||||
- every bug found and how you fixed it
|
||||
- anything you could NOT verify, and why
|
||||
@@ -0,0 +1,73 @@
|
||||
You are Worker F2 on the Bookshelf project (~/bookshelf). Wave 4, part 2:
|
||||
SHOW THE OWNER WHAT IT LOOKS LIKE, AND SHIP AN INSTALLABLE APK.
|
||||
|
||||
FIRST READ (contract — follow exactly, do not restate):
|
||||
~/bookshelf/docs/SPEC.md (see "Design language", "Screens", "Quality bar")
|
||||
~/bookshelf/docs/HANDOFF.md (state + hazards already paid for — read the hazards)
|
||||
|
||||
Waves 1-3 are DONE and verified: 91 unit tests pass, all six screens exist, and there
|
||||
is an existing Paparazzi setup (`ComponentGalleryPaparazziTest`, 10 PNGs of the shared
|
||||
components) that ALREADY WORKS — `~/bookshelf/tasks/gw recordPaparazziDebug` is green.
|
||||
Follow its patterns rather than inventing a new approach.
|
||||
|
||||
Context that shapes this task: there is NO EMULATOR on this box (no KVM). Nothing in
|
||||
this project has ever actually been RUN — only compiled and unit-tested. Your screenshots
|
||||
are the ONLY way the owner can judge how their library app looks, and the signed APK is
|
||||
the only way they can run it for real. Both matter more than usual.
|
||||
|
||||
## Your scope
|
||||
1. **Paparazzi screenshots of all SIX screens** — setup, library, detail, scan,
|
||||
locations, settings — each in BOTH light and dark. Render them with realistic
|
||||
populated state (a plausible shelf of books, covers as placeholder art), NOT empty
|
||||
states, plus the library empty state as its own shot since SPEC calls it out.
|
||||
The scan screen cannot show a live camera in Paparazzi — render its overlay/reticle
|
||||
and the result bottom sheet over a static backdrop, and say so in your report.
|
||||
2. **Release signing + signed APK.** Generate a release keystore, wire a signingConfig
|
||||
into `app/app/build.gradle.kts`, and produce a signed release APK. Keystore and its
|
||||
passwords MUST be gitignored — put credentials in `app/keystore.properties` (gitignored)
|
||||
and read them from the build file, with a graceful fallback so the build still works
|
||||
for anyone without the keystore. Record the passwords in `app/keystore.properties` and
|
||||
state them in your report so the owner can rebuild.
|
||||
`minifyEnabled` is currently false for release — if you enable R8, you must prove the
|
||||
signed APK still builds AND that Room/Retrofit/kotlinx-serialization/ML Kit survive it
|
||||
(proguard rules). If you cannot prove it, leave R8 off and say so.
|
||||
3. **Top-level `README.md`** — what the project is, the two-person shared-library model,
|
||||
architecture (offline-first Room + PocketBase, push-then-pull LWW sync), how to build,
|
||||
how to deploy the server (point at `server/deploy/`), how to install the APK, and an
|
||||
honest "current limitations" section. Reuse the real content in `server/README.md` and
|
||||
`docs/SPEC.md`; do not contradict them.
|
||||
|
||||
## Boundaries — Worker F1 is running RIGHT NOW in this repo
|
||||
- F1 owns `server/` and live-server sync testing, in package `org.modg.bookshelf.livesync`.
|
||||
DO NOT touch `server/` or anything under that package.
|
||||
- F1 may be fixing bugs in data.repo / data.remote / data.local. DO NOT edit those
|
||||
packages. If a screenshot needs a data-layer change, REPORT it instead.
|
||||
- You own `app/app/build.gradle.kts`, the root `README.md`, and all screenshot code.
|
||||
- Do not modify the six screens' behavior to make them easier to screenshot. If a screen
|
||||
is hard to render in isolation, that is a finding to report, not a licence to change it.
|
||||
|
||||
## Build/verify — use the serialized wrapper, never ./gradlew directly
|
||||
~/bookshelf/tasks/gw assembleDebug
|
||||
~/bookshelf/tasks/gw testDebugUnitTest
|
||||
~/bookshelf/tasks/gw recordPaparazziDebug
|
||||
~/bookshelf/tasks/gw assembleRelease
|
||||
|
||||
## Definition of done — actually run these, wait for them, report REAL exit codes
|
||||
1. All four commands above exit 0. `testDebugUnitTest` keeps ZERO failures (91 tests
|
||||
currently pass — do not break them).
|
||||
2. Screenshot PNGs exist on disk for all six screens x light/dark. List their paths.
|
||||
3. A signed release APK exists. Give its exact path, size, and the output of
|
||||
`~/toolchain/android-sdk/build-tools/37.0.0/apksigner verify --print-certs <apk>`
|
||||
(or an explanation if that tool is unavailable).
|
||||
4. Do NOT commit the keystore or its passwords to git. Verify with `git status`.
|
||||
|
||||
## Do not repeat an earlier worker's mistake
|
||||
A worker once ended its turn saying "tests are running in the background, I'll report
|
||||
when done." That is a FAILURE. Run it, WAIT, report observed exit codes. A truthfully
|
||||
reported gap is worth more than a false green.
|
||||
|
||||
## Report (short — read by a token-constrained orchestrator)
|
||||
- real exit codes for all four commands
|
||||
- the screenshot PNG paths, and the signed APK path + size + signer info
|
||||
- anything in SPEC's design language you think the screens get WRONG, now that you can
|
||||
finally see them rendered. Be candid — the owner explicitly cares how this looks.
|
||||
+10
-2
@@ -14,6 +14,7 @@ SENT="$1"; shift
|
||||
TASKS=("$@")
|
||||
L="$HOME/bookshelf/logs"; OUT="$L/$SENT"; LEASE="bookshelf-wave"
|
||||
RENEW="${RENEW:-900}" # renew every 15 min against a 60 min lease
|
||||
POLL="${POLL:-30}" # but CHECK for completion every 30s (see loop below)
|
||||
GUARD_LOG="$L/wave-guard.log"
|
||||
|
||||
lease_hold() {
|
||||
@@ -28,11 +29,18 @@ trap 'lease_release; log "guard exiting, lease released"; exit 0' TERM INT
|
||||
|
||||
log "guard start: sentinel=$SENT tasks=${TASKS[*]} renew=${RENEW}s"
|
||||
lease_hold; log "lease '$LEASE' acquired (3600s)"
|
||||
last_renew=$(date +%s)
|
||||
|
||||
# Poll FREQUENTLY (POLL) but renew SLOWLY (RENEW). Coupling the two, as the first
|
||||
# version did, meant a wave that finished right after a renewal sat undetected for a
|
||||
# full RENEW interval with the sprite pinned hot the whole time.
|
||||
while pgrep -f 'run-task\.sh|run-resume\.sh' >/dev/null; do
|
||||
sleep "$RENEW"
|
||||
lease_hold
|
||||
sleep "$POLL"
|
||||
now=$(date +%s)
|
||||
if [ $(( now - last_renew )) -ge "$RENEW" ]; then
|
||||
lease_hold; last_renew=$now
|
||||
log "lease renewed; workers still running"
|
||||
fi
|
||||
done
|
||||
|
||||
log "workers stopped; writing $SENT"
|
||||
|
||||
Reference in New Issue
Block a user