Skip to content

feat: account settings, ai review as a bonus, schedule wording - #82

Merged
ripgrim merged 22 commits into
mainfrom
fix/schedule-quick-patches
Oct 24, 2026
Merged

ripgrim merged 22 commits into
mainfrom
fix/schedule-quick-patches

Conversation

@ripgrim

@ripgrim ripgrim commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

quick patches after #81, account settings, and ai review as a bonus.

  • schedules read the same everywhere (field, canvas, lane heads, cli, mcp, diff); crons resolve to plain phrases that parse back to the same run times, raw cron only when no phrase fits; "in UTC" for no zone
  • schedule field keeps the saved shape when retyped as equal words, shows the zone for crons
  • hidden tab: next run never shows a past time on return
  • ai review is a bonus: workflows with it turn on, save and push without credits; a run where it did not run takes its verdict from the other rules (no forced human review, not counted by the fail-closed floor); "AI review did not run." stays on run page, check and comment. only the ai review rule itself can't be newly turned on while out of credits
  • credit check: a key's spending cap never reads as ready; ready needs a real account balance
  • passing ai review shows its summary on the run page (redaction respected)
  • account settings in the org settings format: general, sessions, orgs, connections, agents
  • account deletion in 3 steps: turn off tripwire on repos only you can turn off, checks, typed confirm. sole admins of a team org are blocked. team-org history kept without the name
  • cli workflows list shows schedule words

migration: 0032_account_deletion (moderation_items.decided_by on delete set null)

describeSchedule is now scheduleText with a leading capital, and a clock
schedule without a zone names UTC. The canvas node, the lane head, the
CLI and MCP read-backs, the diff and the field's screen reader text all
use it, so a 1 hour interval reads every 60 minutes everywhere.
… save

The editor lints with the AI credit state the rules page reads. A live
workflow drops the issue for a rule step it already runs, with the same
liveSaveIssues filter the server uses, now in contracts.
The minute clock ticks on visibilitychange and pageshow, and every read
computes the current minute, so no render shows a past slot. An enabled
scheduled workflow's detail refetches as soon as the tab is visible.
Ready now needs a readable account balance at or above the floor. The key
limit can only make the state out. With no account balance and room on the
key, the check returns unknown, so an empty account stops flapping ready.
The only admin of a team org is refused. The personal org goes with the account and
its repos are soft-removed. Review decisions keep their outcome and lose the name;
manual re-runs keep the fact through a deleted-account marker.
The summary sits under the rule's name with the same redaction a failing
reason gets, and members also see the model from the trace. The passed
list stays folded and searchable.
…name

Personal-org installs carry their forge, so the deletion flow links GitHub only for
GitHub installs.
…g a repo off by id

Every input is parsed with zod. Deletion needs the typed login, a session under a day
old, no team org left without an admin and no repo left on that only you can turn off.
…tep delete

General, Sessions, Orgs, Connections and Agents tabs. Deleting turns Tripwire off where
nobody else can, lists what changes, then asks for the typed login.
sameCron and cronSpecKey compare what a cron runs, not how it is written,
so 0 9 * * 1-5 and mon-fri match. Schedule timing uses the key, so a cron
that reads back as a preset keeps its place. describeCron goes; the field's
own words replace it.
cronText turns a cron into a phrase in the field's voice, and keeps it only
when the phrase parses back to the same cron: every Sunday at 08:00 in Berlin,
weekdays at 09:00, every 15 minutes, every 2 hours at :30, monthly on the 1st
and 15th at 06:00. Anything else stays the raw cron. The field reads lists of
times and month days and hour steps at a minute, and weekday names and the
hourly minute read as every Monday and every hour at :15 everywhere.
AI review is a bonus. While credits are out, an AI review step carries a
warning, AI review can't run until credits return, instead of an error.
A workflow with it turns on, saves and pushes as usual, and reads Live,
not Can't run. An off workflow shows no AI review issue at all.

liveSaveIssues goes: it only existed to let the old error through. The
rules page still refuses to newly turn the AI review rule on.
isAiReviewNotRunStep names an AI review step that did not run.
… rules

An AI review step that did not run, for credits, a missing provider or a
failure, no longer counts toward the fail-closed floor and no longer
sends a would-be pass to human review. The run, the check and any PR
comment still say AI review did not run, for every one of those causes.
The floor still counts every other rule, and replay counts the same way.
A clock cron without a stored zone shows the zone, like the canvas.
Words that run at the saved schedule's times keep the saved schedule.

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found 1 potential issue (1 medium).

View 2 other findings in Capy.

Open in Capy

Comment on lines +231 to +236
await tx
.select({ id: member.id })
.from(member)
.where(eq(member.userId, userId))
.for("update");
const soleAdmin = await listSoleAdminOrgs(tx, userId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concurrent account deletions can leave a team without admins

Medium Severity

When a team has two admins and both delete their accounts concurrently, each transaction locks only its own user and membership rows. Both calls to listSoleAdminOrgs can therefore count two admins before either deletion commits, after which both users and memberships are deleted. Ordinary members and armed team repos remain, but nobody can administer them. Serialize the check and deletion on shared organization rows, or use serializable transactions with retries, so the second deletion observes the first and is blocked.

Open in Capy

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 2c75d5d and 4931a61 (org row lock in deletion, plus a last-admin trigger for the plugin paths)

deleteUserAccount now locks every org the user belongs to, in id order,
before the sole-admin check. A second concurrent deletion waits for the
first to commit, then sees one admin left and is blocked.
The plugin's remove, leave and role-change hooks check the admin count
and write afterwards, outside one transaction, so two admins acting at
once could both pass. A member trigger now locks the org row and refuses
any delete or demotion that leaves a team org with no admin. The staff
role change locks the same row first, so it still returns its refusal.
@ripgrim
ripgrim merged commit 72ae2ce into main Oct 24, 2026
5 checks passed

This branch was successfully deployed

4 active deployments
tripwire/worker - preview — 4931a614 Deployed Oct 24, 2026 by unkey-deploy[bot]
tripwire/mcp - preview — 4931a614 Deployed Oct 24, 2026 by unkey-deploy[bot]
tripwire/api - preview — 4931a614 Deployed Oct 24, 2026 by unkey-deploy[bot]
tripwire/web - preview — 4931a614 Deployed Oct 24, 2026 by unkey-deploy[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant