wrong fork my fault #74

Closed
jmoran wants to merge 154 commits from jmoran:feat/card-width into main
Collaborator

wrong project branch

wrong project branch
A run now keeps the model, brief and budget its agent had when it
started (run.config, migration 72), so two runs of one task compare on
what they actually used, even after the agent is edited. Readers
without owner facts see it as null, like cost and turns.

scripts/crew-replay.ts is a client of the API: `run` gives each arm of
a spec its own agent and fork of one base, opens the task, waits for
the crew and the check, and writes results, a blind pair file and its
key; `grade` asks a person which side is better and names the arms only
after the last answer.
The crew never works in a mirror, and the repo home hid Fork from a
repo's owner. One person who mirrored a repo had no way to hand it to
the crew.

Offer Fork to the owner when the repo is a mirror. A fork of your own
repo defaults to `<name>-work`, since it cannot share the parent's name.
Commit messages describe the code change. The rule told agents to end
every message with a hardcoded model trailer.
Reviewed-on: jmoran/forgeJ#2
A squash leaves one commit on the default branch. Its message held only
the pull's title, so main's history lost every explanation the pull gave.

- The squash message is now the title, then the pull's description.
- An empty description leaves the title alone, as before.
- A test checks the message keeps the description's overview and bullets.
On a pull, the reviewer's run is also an agent run. A comment or the
reviewer's own verdict steered that run, so the builder never heard
"changes requested" and the pull sat open for good.

- Only the run of the agent the words are for is steered.
- Otherwise a feedback run is queued for the builder, as before.
- A test speaks during a review run and checks the builder is queued.
Claude Code adds its own co-author trailer to every commit by default,
so crew commits carried it even where a repo's AGENTS.md forbids it.

- Each run's settings turn the commit and pull attribution off.
- A test checks the settings say so.
The menu was capped at 480px and scrolled inside itself. Its last
items, Sign out among them, sat below the fold, and macOS draws no
scrollbar to hint at them.

- The menu now grows to the window's height below the top bar.
- It still scrolls on a window too short to hold it.
Reviewed-on: jmoran/forgeJ#1
Reviewed-on: jmoran/forgeJ#3
Reviewed-on: jmoran/forgeJ#4
People name a tell or change by its number, but lists hid it and a
"#N" in prose was plain text, so readers had to open links to know
what anything was.

- List rows show the number after the title.
- A change's head says which tell it answers: "in answer to #6 from jmoran".
- "#N" in issue, pull, comment and review text links to that item.
- The link's hover shows its number, state, kind and title.
- Tests cover linking, escaping, code and links left alone, and the resolver.
Every forge tab held a live event stream open. Browsers allow six
connections to one host over HTTP/1.1, so a few background tabs left
new pages spinning and live updates broken.

- A hidden tab closes its streams: live regions, repository activity and run logs.
- Shown again, live regions and repository activity resume from the last event heard.
- A run log page reloads when shown, which replays its log.
- Checked in Brave: with every tab sent to the background, seven tabs each load in about 30ms; before, the sixth stalled.
A park note written in the owner's name woke a feedback run and steered the
running builder as the owner's word. parkIssue, parkPull and parkForBreaker
now say the note as the run's agent (else a builder on the crew).
Every repository gets Files, Issues, Pull requests and Branches, plus
Settings for an admin. Issue and pull lists filter by open, closed and
all, show the git state badge, the author, and a pull's branches, and
link to /issues/:n and /pulls/:n. The branches page leads with a branch
graph drawn from git log --branches.
ensureDirectionIssue opens the direction issue in the owner's name, so its
issue.opened event seeded "because you told" for every item in the app.
seedOwnActs now skips the issue.opened event of a repo's direction issue.
A run whose subscription's whole-life budget was spent was paused and
queued again as "paused on the provider's limit", so it read as a Claude
usage limit. The resting pick now says when the budget is the cause; the
paused run, its queued successor (BUDGET_PAUSED), its run log and a run
that fails past MAX_PAUSES all name the forge budget, and interruptionOf
reads that failure as a "forge budget" interruption.
The review task quoted only the direction line, so the reviewer asked to
remove work the owner had asked for on the pull. The runner now passes
which commenters direct the crew, and the review brief quotes their words
first and says they outrank the direction.
The review checklist named the terminal door, src/tty.ts and the forge's
signed headers in every repository, games included. Those lines now apply
when the repository is an app the forge runs; others get a plain security
line.
A run paused on a spent forge budget was queued with not_before an hour
out, and neither raising the budget nor Wake cleared it. wakeBudgetPaused
sets not_before to now on every queued agent run whose summary starts
with BUDGET_PAUSED; the Subscriptions budget and Wake handlers call it.
A run still out of budget picks, pauses and waits again.
capReached counted every run started since a director last spoke, so a
healthy reviewer/builder loop reached ISSUE_RUN_CAP in minutes and was
parked though its last round was approved. Runs created before the
newest approving review on the issue no longer count.
Builders left asides for the reviewer in pull descriptions. The system
prompt now says the description is what changed and why, with no notes to
the reviewer and nothing about how the work was done.
A forge with no snapshot destination ran the nightly take anyway and
logged "ERROR snapshot failed: this forge has nowhere to put a snapshot"
every night. The nightly timer now skips the take when no store resolves,
and boot logs one info line, "snapshots off: no destination", in place
of "snapshots on where=nowhere". An on-demand snapshot still fails and
says why. test/snapshot-nightly.test.ts joins GATE.
requeueInterrupted labelled every agent run whose lease lapsed as
"interrupted by a restart", including one this process still held after
the machine slept. It now takes the dispatcher's in-flight map: a lapsed
run still in it is requeued as "lost its heartbeat though the forge never
restarted"; any other keeps the restart label.
# Conflicts:
#	src/crew/brief.ts
spawn-host.test.ts sat in neither gate list, and the stylesheet test
predates the nonce'd boot paint every page now carries.
The Forks and Runs tabs left the strip, so their tests now look for the
palette entry and the forks page's own count.
Home now carries the forge's tabs (Home, Apps, Repositories, Map, Board,
Front page), your apps with links to their issues and pull requests, a
File an issue form with File and File to crew, and the SSH command
folded behind a button. The activity feed stays at /asked. File to crew
lands on the issue it filed and names the builder it assigned.
Reached from the command palette. file and feed are reserved names.
The feed's tests read /feed; the home's read the File an issue form,
your apps and the folded Connect card. File to crew lands on the
filed issue. A new test covers POST /file: a writer lands on the
issue; an unreadable private repository, a blank title, a stranger,
another site and a read-only token file nothing.
Its title says what waits: pull requests and repositories waiting on
your decision.
A writer pins a commit under refs/builds/<label>: an annotated tag with
a note and Parent and Source trailers. No check runs and no agent wakes.
Branches leads with a Builds card: each pin under its named parent, else
under the nearest pin in its history, and a fork's first pins under its
parent repository's. Pin build sits on each branch row and on a pull
request's head; the branch graph marks pinned commits.
A fork's first pin hangs from the nearest of its parent repository's pins
in its history, by the same rule as inside one repository.
An app's demo runs one commit at <slug>--demo with a copy of production's
data. The runtime rebuilds it from refs/demo on boot, lists it after
production in its status, and every page that names an environment says
demo. The slug demo is reserved so <theirs>--demo stays unambiguous.
setDemo moves refs/demo under a lease to a commit a verified writer chose,
announces app.demo and deploys the demo; clearDemo deletes the ref and
tears the environment down. A mirror refuses, a non-commit is refused, an
agent may not pin, and the pre-receive hook keeps refs/demo out of reach of
a push. PUT and DELETE /api/v1/repos/:owner/:name/demo call them.
On an app, Update the project demo to this build calls setDemo after the
pin lands; the pin stands even when the demo does not move. The Builds
card marks the demo's pin and links its address.
Reviewed-on: jmoran/forgeJ#6
Home, Apps, Repositories, Map, Board, Front page and Activity each open
on the same tab strip at the same height, then a title and one line.
The board is a page under the tabs; ?wall keeps it bare and dark.
Home, Apps, Repositories, Map, Board and Front page sit in the topbar of
every signed-in page, lit where you are, so repo pages reach them too;
the per-page strips are gone. A repository's header is the same on every
tab: description and where-row always, no branch count that only some
pages knew, and an App tab for an app's own page. A plain repository's
Files carries the same one-line strip an app's does.
The Builds card draws every pin of the fork family: up to the root and
every readable fork below it. A pin may name a parent in another
repository (owner/name:label), a concept edge drawn with a dashed mark;
that pin is drawn as its root. Without a named parent a pin hangs under
the nearest pin in its history, here or in any repository it was forked
from, nearest first.
The Fork form takes a name, prefilled with <name>-work on your own
repository, and owners are offered it on native repositories too. The
one-fork-per-parent rule still holds for other people's repositories;
an owner may grow several named forks of their own.
Branches gains a Graph view: every pin and branch tip of the fork family
as a node, git history solid, concept edges dashed, branch tips dotted.
Nodes drag; their places stay in the viewer's browser, with Reset.
Picking a node opens a panel with its facts and its verbs: Preview now
on an app the viewer may write, Pin build on a branch tip, its files.
A new environment kind, build-<sha7>, held only in the runtime's memory:
at most three per app, a fourth tearing down the oldest, none rebuilt on
boot. previewBuild (actions/build_preview.ts) asks for one as a verified
writer, never an agent or on a mirror, and announces app.build_preview.
parseAppHost and parseAppLogin read the new label back; slugs shaped like
b<sha7> are reserved the way demo is. Reachable at POST
/:owner/:name/builds/preview and POST /api/v1/repos/:owner/:name/builds/preview.
# Conflicts:
#	src/http/server.ts
1 Builds is the graph as it was. 2 Experiments draws one node per fork
with its iteration count and its best build, the pin its main builds
on; a fork opens to show its iterations, and a closed fork carries their
edges. 3 Generations puts each build in the column of its generation,
joined only by lineage, each fork's iterations together and its best
ringed in colour. Layout and open forks stay in the viewer's browser,
per look.
forge.json app.pages names the pages a person plays or downloads. Each
look's panel offers Play per page, a build preview opened on that page,
and Download per page: GET /:owner/:name/builds/:sha/download?page=,
the page as one .html file fetched from the commit's running preview,
for anyone who may enter the app. Nothing is built or stored for it.
A form's redirect to another origin is held to the page's CSP
form-action 'self', so Play did nothing in the browser. The answer is
now a short page that refreshes to the build preview's address, with a
link to it.
primitive.ts gives every issue, pull request, fork and deployment page
one shape: kind, title, number and state; primary actions and one
Actions menu; info sections that start open; and an Automation panel
for anything the forge does on its own. docs/WORDING.md states the
rules the pages are written to.
The issue page now uses primitiveLayout: kind, title, number, Open/Closed
state and an opened-by byline; Comment and Close issue/Reopen issue as
primary buttons; edit, assign, labels, dependency, milestone, seal,
private fix repository, copy link and origin in the Actions menu.
Details, Assignee, Labels, Milestone, Dependencies, Linked pull requests
and Participants are info sections with their forms. The crew and
autonomy are Automation panels; the studio's word appears only there,
as the inferred state.

The new-issue page says New issue and Create issue, offers labels and an
assignee to writers, and a separate checkbox that assigns the crew's
builder. A comment can close the issue with a reason (Close with
comment). The timeline names assignments and dependency changes.
Rows read "opened <time> by <author>", the assignee, the comment count
and the update time. Filter chips name the field (assignee:, author:,
title contains:). The compose button says New issue; the signed-out
button says Sign in to create an issue; outside reports read as problem
reports.
One header for every tab: kind, title, number, state, and a byline of
facts. Merge pull request (with the merge method) shows only where
mergePull would take it, else the one reason it cannot; Close and Reopen
beside it; every other action in the Actions menu. The rail lists
details, branches, checks, reviews, deployments, labels, assignee,
linked issues and participants. The crew, autonomy and the inferred
state move into Automation panels.

Tabs: Conversation, Commits (new route), Files changed. Edit title and
description is reachable from the menu. Flashes for merge, revert, hold
and release say what failed and what to do. Tests follow the wording.
Deployments carries the preview's state badge, why it sleeps, and Open,
Redeploy or Wake preview. The preview screenshots return to the
Conversation tab. Agent runs are read in one query, so the page costs no
more as a pull's runs grow.
The Forks page is a primitive page: a table of forks (name, created,
ahead and behind the parent's default branch, last push, visibility),
the lineage, and the Fork form labelled "Name of the new fork". A
fork's card says "Fork of X. N commits behind, M ahead. Conflicting
files: ..." and offers "Merge upstream main into main" and "Open pull
request to X"; the crew's merge is an Automation panel inside it.
The primitive layout's rules and the table rules join the stylesheet.
The app's page is a Deployments table: one row per environment
(production, demo, each pull preview, each build preview) with commit,
branch or pin, the runtime's state, URL, started and last deploy run,
and each environment's actions in its row's menu (Open, Redeploy,
Stop, Start, Clear demo, View log, Open terminal). Redeploy production
is the primary; app settings, environment variables and access are in
Actions. The rail lists the app record, the terminal, forge.json, the
contract and seven days of usage; automatic deploys, the crew's runs in
flight and screenshots are Automation panels. POST /app/demo/clear
clears the demo from the web.

The run list is a table (kind, trigger, commit, environment, state,
duration, cost, actor, created) filtered by ?kind= and ?state=, with
Cancel run and Retry run in each row's menu. The run page is a
primitive page with the same facts, Cancel run / Retry run, and the
log. POST /runs/:id/retry queues an agent run's task again or
redeploys a deploy run's environment.
# Conflicts:
#	test/apps-idle.test.ts
#	test/studio.test.ts
# Conflicts:
#	src/web/pages/following.ts
#	src/web/pages/search.ts
#	src/web/studio/actions.ts
#	test/apps-idle.test.ts
#	test/contents-api.test.ts
#	test/following.test.ts
#	test/forks.test.ts
#	test/holds.test.ts
#	test/studio-words.test.ts
#	test/studio.test.ts
#	test/web-ui.test.ts
The Tree look now shows which pins carry on and which stop.

- each repository's next pin sits on the same row, left to right
- a thick accent line marks history that reaches a main branch
- pins no main branch holds, here or downstream, are faded
- the side panel lists what each pin was carried into
- the graph scrolls at full size instead of shrinking to fit
A pin can now name the cartridge it releases, stored in git on the pin itself.

- Cart name, version, producer, page, shelf switch and cover ride as trailers on the pin's tag.
- Changing them rewrites the tag for the same commit, keeping who pinned it, when, and the note.
- An uploaded cover is kept in git as a picture the pin names.
A cart's page is now captured from its build and kept, so it plays without a running preview.

- Capturing starts the build's preview when needed, waits for it, and saves the page and a picture of it.
- Captures sit in the forge's data folder by repo, commit and page, and are never overwritten.
- What a capture under way or a failed one looks like is kept in memory for the panel.
A pinned build now has a cartridge panel beside the build graph, as in mockup F.

- It shows the cover, cart name, version, producer, page and an "On the shelf" switch.
- Writers save the facts, flip the switch and upload a cover. Readers see the facts.
- A cart with no capture yet starts one. Download takes the capture when there is one.
- A cover nobody uploaded is the page's picture, drawn in the house cover style.
A public Arcade page now lists every cart switched on the shelf, from any repository.

- It is the browser arcade as it was: the chunky carousel, the console and the jbox boot tag.
- It answers at arcade on the apps domain, its own address, so carts keep their saves and never run as the forge.
- Carts with the same name are one cartridge, and each shelved pin is one of its builds.
- The forge's top bar links to it as Arcade. No app or account may take the name.
The Arcade's "Export snapshot" link now downloads the whole shelf as one HTML file.

- Every shelved cart, cover and picture goes inside the page, so it plays from disk.
- The file is browser-arcade-<date>.html, as before.
- Its one top line reads "JBOX • snapshot <yyyymmdd> | This is an HTML file that doesn't receive updates".
Pinning is now the release step: a new pin becomes a cartridge with its page saved.

- The pin form ticks "Release it as a cartridge" by default. The Arcade shelf stays off until someone turns it on.
- A branch named iter-N offers iter-N as its label, and a fork's first pin offers iter-1.
- A fork of an app runs as an app too, so its pins can play.
- The API releases a cartridge when asked with "cart": true.
An operator can now bring in cart files that were built elsewhere.

- Each entry pins its commit if needed, puts the cart facts and cover on the pin, and stores the file as its capture.
- It is for games whose offline file comes from their own build, like Krazy Karts, and for moving today's shelf in.
- Run it with bun scripts/import-carts.ts on the forge's data folder. A capture already there is kept.
The picture a cart's cover is drawn from is now taken wide, as games draw themselves.

- A square window left black bars above and below a letterboxed game.
- The cover still crops the middle square.
The shelf now opens on the cartridge pinned first, rather than sorting by day and then by name.

- Carts pinned the same day no longer fall into alphabetical order.
A cartridge on the shelf now has a Play button in its panel.

- Play opens the Arcade on that cart, so it boots through the jbox tag.
- The note about write access no longer shows beside a cart that can be downloaded.
Pins on the build graph can now carry an icon and a note, for showing a build to a given audience.

- The icon sits inside the pin's node. The note's first line sits under it.
- Writers set both in the pin's panel, under "On this graph". Readers see them.
- They are kept beside the forge's data, not in git, and never change the pin.
Conflicts resolved by topic: upstream's behaviour and structure kept,
jmoran's wording, page layout, builds work and crew fixes re-applied on it.

- Migration 72 (run agent settings) moves to a migration file.
- Play opens the build by script, since upstream bans meta refresh.
- Tests make users with the seed origin, as upstream now requires.
- The Asked tab count test goes with the home tabs it tested.
Upstream moved page copy into *_words modules and settings pages, so the
dev UI's wording was lost there in the merge. Each module now says what the
pages said before, and new upstream copy follows docs/WORDING.md.

- Composers and the footer say File an issue or File to crew.
- Decisions, the asks page, follow modes and mirror texts name the action.
- The reviewer brief quotes a director's comment once, as scope.
Most red tests asserted upstream's old wording or markup; they now assert
the dev UI. Where the merge had dropped behaviour, the code is restored in
upstream's structure:

- the stale-check merge gate, team verdicts and the Resolve conflicts door
  on the pull page
- the merge box's hold until green, the flow-only held line and Parked pulls
- the render memo with #N refs, a cached Decisions count, and one read of
  writable apps on the home, to keep page budgets
- builds API list shape, timing and OpenAPI rows for pins and the demo
- a crew pull handed to a person frees its autonomy slot
The pull page drew its checks as info sections with no ids, so upstream's
live client had nothing to swap and the rail changed only on reload.

- The sections carry the ids #pull-check and #pull-preview.
- /pulls/:n/state answers those same sections, so the swap is like for like.
- Checks gains a Checked commit row, which carries data-head for the client.
Four upstream details the dev UI had dropped, in its own words:

- Issue list rows say how a closed issue ended: done, dropped, or merged
  in #N.
- The Contract section's last probe is a green or red badge.
- The home's New issue form keeps its title and body as a draft until filed.
- The form can save the text for the planner, as upstream's composer could.
The merge put upstream's figure tiles, summary line and 'Since you were
here' list above the home's apps and New issue form. They now sit on
/board, which holds the forge's other operator views, and say what they
count: merged pull requests, agent runs, run slots and agent spend.
A pin's cart trailers become demo trailers, so a build of any stack can be
shown, not only a game page:

- Demo:, Version:, Launch:, Page:, Showcase: on and Cover: are written.
  Cart: and Shelf: on still read, and Producer: is left to the Arcade.
- Launch: file plays the captured page, env opens a running build preview,
  shots shows screenshots. forge.json app.launch sets a repo's default.
- scripts/rewrite-demo-trailers.ts rewrites old tags in a data dir: a dry
  run unless --apply, keeping each tag's commit, tagger, date and note.
A pin can carry screenshots, so a feature can be shown without running a
preview. Launch: shots makes them the demo's main view.

- refs/shots/<label> is a git tree of images and shots.json captions.
- Writers upload PNG, JPEG or WebP (2 MB, 12 a pin), or pick the deploy's
  looks pictures; they caption, reorder and remove them.
- Images are served typed by their bytes, nosniff and sandboxed, to anyone
  who reads the repository. Web, API and OpenAPI routes, with an event.
A repository's display.json defines a page of showcased demos: its title,
sources (owners or repos), hand order, per-demo launch, and a layout of
shelf, grid or graph. New demos join the end.

- /<owner>/<repo>/display serves it, with a snapshot export.
- The Arcade host serves the display a site admin names on /admin/limits;
  played pages keep their own origin.
- Screenshots get a gallery at /builds/:label/shots.
Turning Showcase on publishes that one demo: its page, screenshots, cover,
name and version reach anyone who can see a display that includes it. The
repository, its code and its other pins stay private.

- A viewer who cannot read the source sees the name and version only, never
  the owner, repository or a link.
- Display-scoped routes serve a published demo's shots, cover and download
  under an opaque HMAC id that only that display resolves.
- Showcased file demos play on the display origin without a grant; turning
  Showcase off removes them at once.
- The graph shows All, Demos or Showcased (?show=), marks each pin
  Showcased, Captured or Demo, and folds runs with no match into one
  'N builds hidden' node.
- The pin panel opens on its facts and demo summary; settings, screenshots,
  cover, routes and lineage sit closed. An Actions menu removes a pin from
  the showcase, deletes its demo, or deletes the pin, each confirmed, with
  API routes for the deletes.
- /display/edit lists every showcased demo with names and versions, adds and
  removes sources, reorders by drag or buttons, previews the display.json
  diff and saves it as one commit. Capture all captures the displayed set.
The Show filter (All, Demos, Showcased) and the 'N builds hidden' stops now
run in the browser over every pin the page carries, so nothing reloads.

- A hidden run sits in its own thread's column or lane and opens in place;
  an opened run folds back with 'Hide N builds'.
- ?show= still sets the first view and is kept current; ?open= is gone.
- The fold is one pure function the page scripts and tests share.
- The pin panel test reads its sections as details; figure tests read /board.
- The issue list's close-reason lookup counts inside its issues phase.
- Display answers and the snapshot download name session and render.
- With no backup destination, the restore drill is skipped quietly.
- Two tests stop depending on today's date or signup order.
- The top bar holds Home, Apps, Repositories, Displays and the Arcade when
  one is set. Map, Board, Front page and Decisions leave it; their routes
  stay until jmoran rules on them.
- /board no longer draws the figure tiles, flow line or 'Since you were
  here' list that were moved there from the home.
- The top bar is exactly Home, Apps, Repositories and Displays.
- Asks counts are checked on /asks, since the header no longer counts them.
- No page draws the figure tiles or 'Since you were here'; the tests that
  measured them on the board are replaced by checks that they are absent.
- /displays lists every display the viewer can read: title, layout,
  repository and demo count. New display picks a repository you write and
  opens its editor.
- Saving in the editor lands on the finished display page.
- Writers get Edit display on every layout, the shelf included.
- A fork family with showcased pins gets /<owner>/<repo>/iterations: its
  build graph, read only, showcased pins only, linked from Branches.
- The shelf's bar above the arcade gives any signed-in viewer a way back to
  Displays; writers also get Edit display and the save flash.
- A display page lights Displays alone, not Repositories too.
A site admin naming a display for the Arcade publishes it, the way Showcase
publishes a demo: strangers on the Arcade origin see it, and its private
sources stay unnamed. The settings card says so. A repository with no
display.json is still no Arcade. The displays list draws titles as plain
cells.
- /displays is in app.looks and the design audit; iterations is listed.
- The board draws no figures, vitals or digest and records no visit.
- The home's waiting count stands in for the top bar's Decisions count.
- The display page's flash cookie is a named cookie site.
A repository whose fork family holds build pins gets an Iterations tab:
the family as fork cards in columns by fork depth, builds as rows, under
two checkboxes, Only show showcased and Dev notes. Clicking a build plays
it beside an info panel with its pin, commit, branch, lineage, notes and a
reserved row of actions: Download, Copy link, Clone and Fork.

- The graph sits in a scroll pane with a height limit.
- An uncaptured HTML build offers Build and play to a writer.
- Spec: records/design/1031-forgej-iterations/LAYOUT.md.
- A fork's default branch is never listed as a branch with no pin, and a
  fork with no builds lists no branches.
- Another owner's fork is named with its owner, so two nova-spicy cards
  read apart.
- Dev notes shows and hides each build's note line and its dev notes; the
  'main' mark is gone.
- A card header is the fork's name alone, in full; the owner is added only
  when two forks in the family share a name.
- Download, Copy link, Clone and Fork each open a dialog that says what
  happens first. Fork names the new repository, says it copies branches
  and tags but not build pins, and lands on the new fork's Iterations.
Each fix answers one audit finding (records AUDIT-1101.md):

- Browser edits keep LF; an empty repository takes its first file (R16, R17).
- No Merge on a conflicting pull's card; empty squash names no commit;
  Resolve conflicts follows the pick (R5, R32, R38).
- Deployments has its own URL and the 404 knows who is signed in; the ask
  line links what it counts (R14, R46).
- Graph displays draw the family; grid cards show a picture and play in
  place (D7, D5).
- Every graph look gets the full pin panel; a missing Play or failed
  capture says why (G2, G3, G12, G24).
- Build and play lands on the player; a fork lands on its card and its
  flash no longer promises pins (I6, I11).
- Smaller wording, landing and layout faults: R3, R8, R11, R13, R21, R37,
  R49, R50, G9, G18, G22, G26.
Decided 11-01 after the audit (I3, G27, R26, R39):

- The Iterations info panel's Pin note and Dev notes have a quiet Edit for
  writers. Saving rewrites the pin's tag message, keeping its tagger, date
  and trailers, and appends build.noted; also PATCH on the builds API.
- An issue's Milestone and a pull's Labels get the rail's existing picker
  behind a small gear, for people who may change them.
Decided 11-01 (PRD step 4):

- A pin's note and dev notes live in <label>.md on refs/forge/notes in the
  pin's repository; each edit is a commit, so history is kept. Pins with no
  file still read their tag message.
- display.json may list builds: the display's chosen builds in order,
  showcased or not. Without it, today's rule holds. PUT .../display/builds.
- Forks copy refs/builds, refs/covers, refs/shots and refs/forge/notes;
  the Fork and Clone dialogs say so.
As confirmed in records design/1101-forgej-displays (LAYOUT.md):

- The display page has a build sidebar for writers: every pinned build in
  its sources, by fork, ticked in or out; it folds, and so does each fork.
- The info panel sits top-left beside the player; the builds sit below as
  cards with pictures, dragged to reorder. Ticks and drags save builds.
- Iterations has one selection picker: All iterations, Showcased and each
  display. Edit display selections gives every build a chip per selection,
  and the info panel lists the selections a build is in.
- The JSON-diff editor page is gone.
/<owner>/<repo>/display is now round 1101 E's page for a shelf display too.
The carousel shows on the Arcade host, or at ?look=shelf; ?edit=1 is gone.
Decided 11-01:

- A build has one Notes section, markdown, edited as a whole; the
  Iterations checkbox is Notes. PATCH builds takes notes and still joins
  note and body.
- The Arcade serves the newest display titled arcade-builds in a site
  admin's repository; the /admin/limits setting is gone.
The display page has a quiet Edit beside its title; Save writes only the
title to display.json as one commit (POST .../display/title). Naming an
admin's display arcade-builds makes it the Arcade.
A build's Notes section is now one list, kept as notes.json on
refs/forge/notes: { "notes": [ { iteration, by, kind, at, text } ] },
the format ui-iterator keeps beside a round. iteration is the pin label.
Each note is appended in one commit by its author; a correction is a new
note. notes.lock holds every note's fingerprint (sha1 over the five
fields, first 12 hex), and an append refuses a notes.json that lost one
(notes_lost).

People's notes rank first and are highlighted, then agents' notes, then
the tag's own note. Each note shows its first line as a title; the rest
opens on a click. A writer adds a note in place with Add a note. An
agent on the repository's crew may add one too, as kind agent under its
own name.

An older <label>.md notes file reads as one note by whoever last wrote
it, and the next append folds every such file into notes.json. Deleting
a pin keeps its notes. PATCH builds/:label and the web form now append;
note and body still join into one note.

When FORGE_NOTES_INTENT names a doc, a person's note is copied into it
once, under "## Iterator notes", in ui-iterator's line format with its
hidden fingerprint (core/intent_notes.ts). Agent notes are never copied.

Also fixes the panel's Save: the Iterations page root carries data-edit,
so a click on Save was taken for an Edit click and never submitted.
Iterations gains a Columns | Tree switch (?layout=tree); Columns stays
the default. Tree is round 1101-forgej-graph-layout's B: the family as
runs that grow downward. A build continues its parent's run when the
parent is that run's last build in the same repository; otherwise it
starts a run of its own, hung just under the build it grew from (a
fork, a branch, or a concept edge). A fork with no build to hang from
hangs under its parent fork's head. Runs are drawn depth first, the
child that starts nearest a run's foot first (lanesOf).

A fork folds to one node by its name, and a run with branches beside
it folds to "first → last". Folds of builds outside the selection keep
working, and a line starts at the fold that holds its build.

From C and D: a run can be dragged anywhere. Where runs were dragged is
kept in this browser only, per repository (localStorage). Reset puts
every run back in the tree's own place; Align keeps the dragged order
and snaps columns and spacing to a grid. A drag is never taken for a
click.
Branches → Graph is gone: Iterations' Tree layout replaces it
(records/design/1101-forgej-graph-layout). An old /branches/graph link
301s to /iterations?layout=tree, its ?pin= picked as build=.

Of the graph panel's tools, only Delete pin moves: it is now in the info
panel's Actions, behind a dialog, for a person who writes the
repository. It deletes the pin with its cover and screenshots, keeps the
commit and its notes, and lands on Iterations.

The rest stays behind, by jmoran's call to keep the forge to its proven
parts: the demo settings form, cover upload and Use generated cover,
Remove from showcase and Delete demo (the Showcased chip and the API
cover them), the screenshot upload, pick, caption, move and remove forms
(the API still writes screenshots), and graph marks with their icon
route and store. Demo facts, covers and screenshots are still read where
they exist: a display's shelf, a cover, a picture.

Pinning a build now lands on Iterations with the new pin picked. Branches
shows an Iterations link when the repository holds pins, in place of
Graph. The confirm page drops the removed verbs.

Tests move with it: the graph, marks and cart panel suites go; screenshot
writes are tested through the API; the pin and checkpoint flows assert
the Iterations landing. Build info's capture wait joins the no-polling
allowlist with its reason.
Test connection on Backups ran the snapshot store's five retries, so a
refused endpoint answered only after 7.5 s of waits. It now makes one
attempt (s3Store's attempts option), and says so at once.

The Backups page follows a snapshot by its events (data-live), not a
meta refresh: its test now says that, and reads until the run has put
an object instead of racing the first one.

An Instagram prompt's issue body no longer repeats its title, since
the 11-01 audit fix to tell(): the test now expects that.
The graph grows off the page fast. Beside its key, both Columns and Tree
now carry − and + (fixed steps from 25% to 200%), the scale itself (a
click goes back to 100%), and Fit, which shows the whole graph in the
stage. ⌘ or Ctrl with the wheel, a trackpad pinch, zooms where the
pointer is. The scale is kept in this browser.

The graph scales as one transform inside a box sized to it, so the stage
scrolls to the scaled size. Lines and drags divide by the scale, so a
line still meets its card and a run follows the pointer at any zoom.
Cards in both layouts are now as wide as a conventional commit subject,
50 characters of monospace plus padding, and every card's text is
monospace. The tree reads a run's width from the page, so its columns,
Align and lines follow.

Fit read the stage's current height, and a stage that had shrunk to a
small graph shrank it again on every click, one percent at a time. It
now measures the height the stage may grow to, so a second Fit changes
nothing.

Collapse forks folds every fork to its name, and Expand forks opens every
one (and every folded run in the tree). In Columns, a card's name now
folds and opens that card too; a card the selection closed still opens
on its name.
A fork or run folded on Iterations stays folded on the next visit, kept in
this browser per repository and layout, beside the zoom and dragged runs.
Each kept fold remembers how many pins its fork had; a fork that gained or
lost one since opens again, so new work is never hidden behind an old fold.
A link to one build opens the folds around it.
Reviewed-on: jmoran/forgeJ#11
Beside Collapse and Expand forks, both layouts now carry − , the width
and + for the cards: 24, 32, 40, 50, 60 or 72 characters of monospace.
The text keeps its size, so narrow cards stay readable where zooming out
made them hard to read; the tree re-places its runs to the new width.
The width is kept in this browser; a click on it goes back to 50, a
conventional commit subject.
A captured build that was not showcased showed "not found" in the
player, with no button to build it again: the display origin served a
cart only when its pin was showcased, so the grant Iterations signed for
it was refused. The origin now serves any captured build its grant
vouches for, a pin with no demo at the page its file names; with no
grant, still only a showcased demo of a public repository plays.
Iterations signs a grant for every build that needs one: a private
repository's, or one not showcased.
A captured build's player now carries Rebuild beside Full screen, for a
person who writes the repository: it captures the page again from the
pin's commit. A commit's page is still written once everywhere else;
Rebuild alone replaces it, staged and renamed over, so the old page
plays until the new one lands, and stays if the rebuild fails. While it
runs the build reads as capturing.

A build in a repository that is no app said only "Nothing to play".
Its admin now gets Run as an app there, the same door to
/settings/app that the repository's Files page offers.
- Width and Zoom are number fields: type any width from 16 to 100
  characters or any zoom from 20% to 200%; − and + step 4 characters or
  10 points. The number no longer jumps to a default on a click.
- Collapse and Expand forks are one toggle that says how the forks
  stand: All forks open, All forks folded, or N of M forks folded. A
  click folds them all, or opens them all once every one is folded.
- A card or run shows a fold mark (open or folded) and (N) beside its
  name when it holds more than two builds.
- The graph's pane has a set height that its corner drags, kept in this
  browser, so folding forks no longer pulls the player and info up.
  Hide graph folds the pane away, kept in this browser too.
Build info in three groups, and a boundary to resize it beside the player
Some checks failed
preview / preview (pull_request) Has been cancelled
113b04a446
Build info, on Iterations and a display page alike, now reads as three
groups, in order, so it stays readable as features accumulate:

- Fixed, marked read-only: pin, commit, branch, repository, lineage,
  pinned by. What the pin and git settled.
- Yours to change: Showcased with its own toggle, the displays it is in
  with an Edit that opens the selection chips, and Notes. Marked
  read-only for a reader.
- Build: what it plays, then Rebuild, Download, Copy link, Clone, Fork
  and Delete pin. Rebuild and Download move here from the player, which
  keeps Full screen.

A new detail joins the group it belongs to; the order is in the code.

On Iterations, the boundary between Build info and the player drags,
or moves 16px at a time with the arrow keys (a double click puts it
back), kept in this browser. The panel's Showcase toggle lands back on
the page it was pressed on.
jmoran changed title from PR #11 followup: better graph controls to wrong fork my fault 2026-11-02 22:49:53 +00:00
jmoran closed this pull request 2026-11-02 22:50:20 +00:00
Some checks failed
preview / preview (pull_request) Has been cancelled

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
trevato-apps/forge!74
No description provided.