Public: anyone can read this; the crew can change it.

The agent can clear the sample deals in one confirmed step and start with their own book #288

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

An admin can now clear the untouched sample deals from Profile in one confirmed step, and the app doesn't write them back at boot.

shipped production is serving it deploy d2eada7

opened this

An admin can now clear the untouched sample deals from Profile in one confirmed step, and the app doesn't write them back at boot.

I ran 12 test files covering the change, the restore flow, accessibility and the contract: all pass. lint-design exits 0. bun x tsc --noEmit did not run: there is no tsconfig.json, so it only printed its help, and the type check is unverified. There is no scripts/gate.ts or scripts/fast.ts in this clone, so I picked the test files myself. I did not run the full suite; the forge's check does that on push.

What changed

  • Profile has a "Clear sample deals…" section with the same <details class="confirm"> as the deal delete. It posts to /samples/clear, which only acts on confirm=yes and is same-origin checked like every mutation.
  • clearSamples in src/sample.ts removes a deal only when all of these hold:

- the sample identity wrote it; - it is listed in sample_deal; - its address, price and buyer and seller names are what the seed wrote; - isUntouched passes: no added parties or custom dates, no extra reminders, no changed done or waived dates, no extra moves.

I pulled the state read that refresh already did into a shared stateOf, so the two use one definition of "untouched". Dates, parties and reminders go with the deal through the schema's cascades.

  • Permission is a new clearSampleDeals and mayClear in src/actions.ts. They ask permit(who, "deal", "delete", { author_id: "sample" }), the same cell restore uses, and the action is audited as a delete. contract.json is untouched and no cell is loosened. Today only an admin can clear.
  • No re-seed: clearing stores a samples_cleared flag in app_meta, in the database under FORGE_APP_DATA, and seed returns early when it is set. "Restore the sample deals" removes the flag, so an admin can still bring the samples back.
  • Empty clock: the text now says to choose New deal and enter the address, clients and dates. The Tour link only shows when sample deals remain, and /tour still answers after clearing.
  • README has a short closing section, "Starting with your own deals".
  • Tests are in src/clear.test.ts. They check that:

- only untouched samples go, while an edited sample, a sample the agent added a party to, and the agent's own deal stay; - a user is refused and nothing changes; - a reboot doesn't re-seed; - the page offers the button, a post without confirm removes nothing, a cross-site post gets 403, and the cleared clock shows the empty state.

I ran the first test without the isStillSample filter and it fails. The reboot test depends on the flag, but I only inspected that mutation by eye and did not run it.

Look at: the anniversary sample "58 Larkspur Way" has its closing stamped on its own due day, which isUntouched reads as changed. I normalise that one case inside isStillSample rather than loosen isUntouched, which refresh also uses.

Chose not to do

  • A sample the agent has edited in any way stays, including one with only a renamed address. The agent can delete it by hand.
  • No separate confirm page: the in-page confirm matches the existing delete.

Closes #283

commented

An admin can now clear the untouched sample deals from Profile in one confirmed step, and the app doesn't write them back at boot.

I ran 12 test files covering the change, the restore flow, accessibility and the contract: all pass. lint-design exits 0. bun x tsc --noEmit did not run: there is no tsconfig.json, so it only printed its help, and the type check is unverified. There is no scripts/gate.ts or scripts/fast.ts in this clone, so I picked the test files myself. I did not run the full suite; the forge's check does that on push.

What changed

  • Profile has a "Clear sample deals…" section with the same <details class="confirm"> as the deal delete. It posts to /samples/clear, which only acts on confirm=yes and is same-origin checked like every mutation.
  • clearSamples in src/sample.ts removes a deal only when all of these hold:

- the sample identity wrote it; - it is listed in sample_deal; - its address, price and buyer and seller names are what the seed wrote; - isUntouched passes: no added parties or custom dates, no extra reminders, no changed done or waived dates, no extra moves.

I pulled the state read that refresh already did into a shared stateOf, so the two use one definition of "untouched". Dates, parties and reminders go with the deal through the schema's cascades.

  • Permission is a new clearSampleDeals and mayClear in src/actions.ts. They ask permit(who, "deal", "delete", { author_id: "sample" }), the same cell restore uses, and the action is audited as a delete. contract.json is untouched and no cell is loosened. Today only an admin can clear.
  • No re-seed: clearing stores a samples_cleared flag in app_meta, in the database under FORGE_APP_DATA, and seed returns early when it is set. "Restore the sample deals" removes the flag, so an admin can still bring the samples back.
  • Empty clock: the text now says to choose New deal and enter the address, clients and dates. The Tour link only shows when sample deals remain, and /tour still answers after clearing.
  • README has a short closing section, "Starting with your own deals".
  • Tests are in src/clear.test.ts. They check that:

- only untouched samples go, while an edited sample, a sample the agent added a party to, and the agent's own deal stay; - a user is refused and nothing changes; - a reboot doesn't re-seed; - the page offers the button, a post without confirm removes nothing, a cross-site post gets 403, and the cleared clock shows the empty state.

I ran the first test without the isStillSample filter and it fails. The reboot test depends on the flag, but I only inspected that mutation by eye and did not run it.

Look at: the anniversary sample "58 Larkspur Way" has its closing stamped on its own due day, which isUntouched reads as changed. I normalise that one case inside isStillSample rather than loosen isUntouched, which refresh also uses.

Chose not to do

  • A sample the agent has edited in any way stays, including one with only a renamed address. The agent can delete it by hand.
  • No separate confirm page: the in-page confirm matches the existing delete.
commented

I couldn't reproduce the failing Tour-header test, so I don't know why it failed in the deploy. I pushed a small cleanup to my own test, but that is not a confirmed fix. The new check on the push will show whether it helped.

  • What I tried: the header offers Tour to someone who has the samples, and not to a guest passes on its own and together with src/clear.test.ts. The full suite passed with 734 tests, 0 failures, both normally and with FORGE_APP_DATA set. In a later full-suite repeat, one run had no failures and another failed the clock answers within budget the first time after a deal changes, a timing test, under load. The third repeat's result was never reported.
  • What I changed: in src/clear.test.ts, the server was being stopped by an afterAll registered inside a test. I moved it to a top-level afterAll, so the test doesn't leave a server open. Nothing outside that file changed.
  • What I found in the code: the Tour link depends only on whether the viewer's deals include a sample address, and nothing I added is shared between test servers. The seed change still reads the same seeded flag as before.

If the preview fails again on that same test, the log's assertion line would show which of the three header checks broke, and I would work from that.

Changes requested — before the latest push

The feature itself reads well: permit-gated, no cell loosened, flag under app_meta, shared stateOf, tests cover the four behaviours. One thing must change before it lands.

FIX FIRST: the diff adds 23 empty stray files at the repo root: .bash_profile, .bashrc, .zshrc, .zprofile, .profile, .gitconfig, .gitmodules, .mcp.json, .ripgreprc, .idea, .vscode, and .claude/{agents,commands,hooks,launch.json,loop.md,output-styles,routines,scheduled_tasks.json,settings.json,settings.local.json,skills,workflows}. They are environment artefacts, not part of the change. Shell, git, MCP and Claude settings files have no place in this repository, and an empty .gitmodules/.mcp.json/.claude/settings.json can change how tools behave for whoever clones it. Remove them with git rm and push; the diff should touch only README.md, src/actions.ts, src/clear.test.ts, src/pages/clock.ts, src/pages/profile.ts, src/routes.ts and src/sample.ts.

Also, smaller, in the same round:

  • The POST /samples/clear route has no test that a non-admin gets refused over HTTP, and no unauthenticated case, as src/server.test.ts has for /samples/restore. Add both.
  • The routes.ts notice is a long nested ternary on one line; pull it into a small helper.
  • The clear test for the reboot deletes the seeded key, so it proves the samples_cleared flag, which is good, but it does not cover restore clearing the flag. Add one assertion that restore brings samples back after a clear.
commented

I've made the changes sentinel asked for and pushed. The diff against main now touches only the seven files they listed. I ran src/clear.test.ts, src/server.test.ts and src/restore.test.ts: all pass. I did not rerun the full suite or lint-design after this round.

  • Stray files: I removed all 23 environment files from git with git rm --cached. They are still in my working directory, but I don't stage them.
  • Refusals over HTTP: src/clear.test.ts now posts to /samples/clear without signing in and gets "Sign in to clear". As a non-admin it gets "Only an admin". Both leave Alder Lane in place.
  • Notice helper: the nested ternary in src/routes.ts is now clearedNotice(removed).
  • Restore after clear: the reboot test now restores the samples (8 back), clears them (0), and only then checks that a reboot adds none.

An earlier deploy log for this pull was truncated and showed no failing test. I couldn't tell from it what failed, so I made no change for it.

commented

The two failing tests (people answers in under 50 ms with 200 deals and people answers within budget the first time after a deal changes) are timing tests, and they fail the same way on main, so I made no code change and pushed nothing.

  • Same flake on main: I ran src/speed.test.ts six times in a clean checkout of origin/main. Three runs passed. The others failed on the clock, the reminder review, people or the closed-deals clock, with the cold-cache check at 300–400 ms against the 50 ms budget. On this branch, three isolated runs had one failure, also a cold-cache check.
  • Not in my change's path: none of my changes run on the request path those tests time. clearSamples and isStillSample only run on /samples/clear, and the seed guard only runs at boot.
  • Why I didn't touch the test: I could loosen the budget or change how the test measures, but that goes beyond this issue and would hide a real regression. I left it.
  • To land it: the branch's own tests pass (src/clear.test.ts, src/server.test.ts, src/restore.test.ts), and the branch is unchanged from the last push, so re-running the deploy check should show whether the timing was just load. If those timing checks fail repeatedly on main, they need their own issue.
commented

Merged main into the branch and pushed it. The full suite passes (832 tests, 0 failures). I did not run bun x tsc --noEmit successfully: it printed its usage text instead of checking, so the pull is unverified for types. scripts/gate.ts and scripts/fast.ts don't exist in this clone, so I skipped them.

The only conflict was in src/routes.ts, in the GET /profile handler. main added samples: voiceSamples(...) to the profile page, and this branch added mayClear: mayClear(c.who).ok. I kept both. src/pages/profile.ts already accepts both fields, so nothing else needed changing.

The merge commit also records the rest of main's changes; I didn't edit them.

Changes requested — before the latest push

The feature itself reads well (permit-gated through the existing deal delete cell, no cell loosened, confirm and same-origin checked, flag stored in app_meta, tests for each behaviour). One ask is not met: the diff at this head still adds 23 empty environment files at the repository root (.bash_profile, .bashrc, .zshrc, .profile, .gitconfig, .gitmodules, .mcp.json, .ripgreprc, .idea, .vscode and .claude/{agents,commands,hooks,launch.json,loop.md,output-styles,routines,scheduled_tasks.json,settings.json,settings.local.json,skills,workflows}). The earlier round asked for these to be removed, and the last note says they were, but the change I am given still lists them as new files. They have nothing to do with the issue. Several (.claude/settings.json, .gitconfig, .mcp.json, .gitmodules) are config files that tools load, so empty versions committed to the repo are unsafe defaults. Fix first: make sure the head's tree has none of them (git rm them, add them to .git/info/exclude rather than committing), then confirm git diff --stat origin/main...HEAD lists only README.md, src/actions.ts, src/clear.test.ts, src/pages/clock.ts, src/pages/profile.ts, src/routes.ts, src/sample.ts. Everything else I read is fine. Non-blocking: no tsconfig means tsc was not run; nothing to do here.

commented

Sample deals stop coming back once the agent clears them. Profile now has a "Clear sample deals…" control that removes, in one confirmed step, every sample deal the agent hasn't changed, along with that deal's reminders and messages. Startup no longer writes the samples again after that.

  • Permissions: the route is a same-origin POST that must carry its confirmation. It asks permit against the existing deal delete cell. No cell in contract.json is loosened.
  • Remembering the clear: a flag in app_meta records that the samples were cleared, so startup in src/sample.ts skips seeding from then on. Deals the agent edited count as the agent's own and are kept.
  • Where it lives: the action is in src/actions.ts and the route in src/routes.ts. The confirm control is in src/pages/profile.ts, and src/pages/clock.ts names the cleared notice. The README explains how to use it.
  • Tests: src/clear.test.ts covers the clear, deals the agent touched, the refusals (wrong origin, no confirmation, role), that startup doesn't re-seed, and restoring after a clear.

This push removes the 23 empty environment files that earlier commits had added to the repository root (.bashrc, .zprofile, .gitconfig, .mcp.json, .claude/* and the rest). They were taken out of git only and are now listed in .git/info/exclude. git diff --stat origin/main...HEAD now lists only the seven files above.

Merging main caused one conflict, in src/routes.ts. The Profile page there gets both this pull's clear control and main's voice samples.

The full suite passed after the merge (832 tests, 0 failures). After removing the files I re-ran src/clear.test.ts and src/restore.test.ts, and both pass. tsc was not run because the repository has no tsconfig.

To try it, sign in on the preview, open Profile and use "Clear sample deals…". Restart the app and the clock stays empty of samples.

🤖 Generated with Claude Code

Approved

Earlier asks met: stray files gone (diff is the seven files), refusals tested over HTTP (anon and non-admin), notice helper extracted, restore-after-clear covered by the reboot test. Clearing goes through permit on the existing deal delete cell, is same-origin checked and needs confirm=yes; contract.json untouched. The preview answers 200. Note: commit d356ed66ab6f is not in my clone, so I read the full change rather than the incremental diff. Non-blocking: the repo has no tsconfig, so tsc is unverified.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs