From 0e98af3b5d1b45d5019fa593bdb15ff1979ba8a5 Mon Sep 17 00:00:00 2001 From: Junwen Yin <39627445+junwen94@users.noreply.github.com> Date: Fri, 9 Oct 2026 20:38:24 +0100 Subject: [PATCH 1/2] Point repo references at stfc/goldilocks-agent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit junwen94/goldilocks-agent was deleted and re-forked from this repository. Before that it was private, so its links were 404 for everyone but the owner, and its ghcr image couldn't be pulled anonymously. - The web app's "GitHub Issues" link now opens stfc/goldilocks-agent issues. - The local docker-compose.yml defaults to ghcr.io/stfc/goldilocks-agent, which is public. A fork can still override the owner with GOLDILOCKS_AGENT_IMAGE_OWNER. - AGENTS.md, README, docs, mkdocs repo_url and workflow comments are updated. References to the deleted issue #1 now point to design doc §13. Two dated historical notes in the design and plan docs are left as written. Closes #12 --- .github/workflows/docker-publish.yml | 11 +++++------ AGENTS.md | 3 +-- README.md | 8 ++++---- app/src/App.tsx | 4 ++-- docker-compose.yml | 11 +++++------ docs/getting-started.md | 2 +- docs/index.md | 4 ++-- mkdocs.yml | 2 +- 8 files changed, 21 insertions(+), 24 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 182ffc4..ac052b5 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -1,8 +1,7 @@ name: Build and publish Docker image # Publishes ghcr.io//goldilocks-agent (whichever repo this runs in -- -# junwen94/goldilocks-agent and stfc/goldilocks-agent both use this same -# file) so `docker compose pull` (or a plain `docker pull`) gives a +# stfc/goldilocks-agent, or a fork under its own owner) so `docker compose pull` (or a plain `docker pull`) gives a # colleague a working image with no local clone/build/toolchain needed -- # only `main` and version tags publish; other branches/PRs only run the # `test` gate below. @@ -68,10 +67,10 @@ jobs: - id: meta uses: docker/metadata-action@v5 with: - # Derived from the running repo, not hardcoded to junwen94's -- - # this same workflow also runs on stfc/goldilocks-agent (which has - # its own GITHUB_TOKEN, scoped to ghcr.io/stfc/*, not junwen94/*) - # and a hardcoded owner would just fail to push there. + # Derived from the running repo, not hardcoded: a fork runs this + # same workflow with its own GITHUB_TOKEN, scoped to its own + # ghcr.io//* namespace, where a hardcoded owner would just + # fail to push. images: ghcr.io/${{ github.repository }} tags: | type=raw,value=latest,enable={{is_default_branch}} diff --git a/AGENTS.md b/AGENTS.md index 18c7c02..944e47a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,8 +19,7 @@ Frontend lives in `app/` (Node/Vite/React, not `uv`) — `cd app && npm install` (a form-only tool, no LLM) with its own repo/Docker image, even though `app/` now embeds its published Workbench UI as an npm dependency behind the DFT Workbench Tool's inline panel and full-page detail view alike (a -grid full-page, one card at a time as tabs inline) — see design doc §13 and -[goldilocks-agent#1](https://github.com/junwen94/goldilocks-agent/issues/1). +grid full-page, one card at a time as tabs inline) — see design doc §13. ## Code style diff --git a/README.md b/README.md index b2fc624..766c8e5 100644 --- a/README.md +++ b/README.md @@ -106,11 +106,11 @@ no Python/Node toolchain -- `build: .` in `docker-compose.yml` is only a local-dev fallback (`docker compose up --build` forces a rebuild from source, which does need a real clone). Add `GOLDILOCKS_AGENT_IMAGE_OWNER`/`GOLDILOCKS_AGENT_IMAGE_TAG` to that same -`.env` if you want `junwen94`'s build instead, or a specific commit's +`.env` if you want a fork's build instead, or a specific commit's `sha-xxxxxxx` tag rather than `latest` (only useful for reproducing one exact build -- for everyday use, `latest` already tracks the newest -successful publish automatically, no manual bumping needed). Both repos -publish from the same source, just to different `ghcr.io` namespaces. Once +successful publish automatically, no manual bumping needed). A fork +publishes the same source to its own `ghcr.io` namespace. Once you've picked an engine, open . What's on by default in this image, no extra config needed: chat, **Find in @@ -277,7 +277,7 @@ full-page (the Tools panel's own expand-all-tools button, top right, then DFT Workbench from the six-Tool grid) -- both render the exact same embedded goldilocks-core Workbench UI, published as an npm package and installed into `app/`'s dependencies (see -[issue #1](https://github.com/junwen94/goldilocks-agent/issues/1)): the +`docs/goldilocks-agent-design.md` §13): the full-page view shows all its cards (Structure/Analysis/Advisors/Bundles) side by side, the inline panel shows the same cards as tabs, one at a time. The first time either is opened, goldilocks-agent auto-starts diff --git a/app/src/App.tsx b/app/src/App.tsx index 2b2d583..e611fd4 100755 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -1788,7 +1788,7 @@ export default function App() { const [hoveredHandle, setHoveredHandle] = useState(null); // One workspace/client instance per app lifetime -- recreating it every // time DFT Workbench's detail page is opened would drop in-progress state - // (see junwen94/goldilocks-agent#1 -- this is the embedded + // (see design doc §13 -- this is the embedded // goldilocks-workbench package DFT Workbench's full-page detail and inline // panel both reuse, see CoreWorkbenchTree/CoreWorkbenchTabs). const coreWorkspace = useMemo(() => createCoreWorkspace(new HttpCoreClient()), []); @@ -9940,7 +9940,7 @@ export default function App() {

{t("contribute_heading")}

{t("contribute_desc")}

- + {t("github_issues")} GOLDILOCKS_AGENT_IMAGE_TAG=sha-xxxxxxx docker compose up -d`. + image: ghcr.io/${GOLDILOCKS_AGENT_IMAGE_OWNER:-stfc}/goldilocks-agent:${GOLDILOCKS_AGENT_IMAGE_TAG:-latest} pull_policy: always build: . # `host.docker.internal`: reaches Ollama running *natively* on your own diff --git a/docs/getting-started.md b/docs/getting-started.md index 2f17dbc..c9e6816 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -40,7 +40,7 @@ git clone && cd goldilocks-agent docker compose up ``` -`docker compose up` pulls the `agent` image from `ghcr.io/junwen94/goldilocks-agent` +`docker compose up` pulls the `agent` image from `ghcr.io/stfc/goldilocks-agent` (built and published automatically on every push to `main` -- see `.github/workflows/docker-publish.yml`) instead of building it locally, so this is fast even on a machine with no Python/Node toolchain at all. diff --git a/docs/index.md b/docs/index.md index 070e8f4..7d94c24 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,9 +23,9 @@ not built. This site covers running and configuring goldilocks-agent. For the full product design and a dated log of what's actually been built vs. still planned, see -[`docs/goldilocks-agent-design.md`](https://github.com/junwen94/goldilocks-agent/blob/main/docs/goldilocks-agent-design.md) +[`docs/goldilocks-agent-design.md`](https://github.com/stfc/goldilocks-agent/blob/main/docs/goldilocks-agent-design.md) and -[`docs/goldilocks-agent-implementation-plan.md`](https://github.com/junwen94/goldilocks-agent/blob/main/docs/goldilocks-agent-implementation-plan.md) +[`docs/goldilocks-agent-implementation-plan.md`](https://github.com/stfc/goldilocks-agent/blob/main/docs/goldilocks-agent-implementation-plan.md) in the repository -- internal working documents, not part of this site. ## Layout diff --git a/mkdocs.yml b/mkdocs.yml index 9a04b7a..f923f4d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,7 +2,7 @@ site_name: goldilocks-agent site_description: >- LLM orchestration, feedback-driven scientific workflow, and AiiDA execution layer for goldilocks-core. -repo_url: https://github.com/junwen94/goldilocks-agent +repo_url: https://github.com/stfc/goldilocks-agent repo_name: goldilocks-agent # `docs/` also holds the internal, dated design log (Chinese-language, From d1d11167c128c97069e2ebf740ace72fafe47717 Mon Sep 17 00:00:00 2001 From: Junwen Yin <39627445+junwen94@users.noreply.github.com> Date: Fri, 9 Oct 2026 21:15:03 +0100 Subject: [PATCH 2/2] Replace bare issue numbers from the deleted repo with their fixing commits The "#3/#4/#5/#6/#7/#9" comments and README's "see issue #1" referred to junwen94/goldilocks-agent, which was deleted. On this repository the same numbers are unrelated PRs (#1 is the Ollama-backend PR). The comments now name the commits that fixed those bugs (cc5f0dc, c7bfc57), whose messages name the original issues. Refs #12 --- README.md | 6 +++--- app/src/App.tsx | 20 ++++++++++---------- src/goldilocks_agent/graph.py | 8 ++++---- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 766c8e5..2755ec3 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ submission). src/goldilocks_agent/ Python package (LangGraph orchestration, local HTTP/SSE server, Tools) app/ React/Vite frontend (chat + Tools panel; DFT Workbench's inline panel and full-page detail both embed goldilocks-core/web's published UI -- - see issue #1) -- not "web": see design doc §13 + see design doc §13) -- not "web" mlip-cli/ Own project (own pyproject.toml), just a `janus-core[mace]` dependency pin -- keeps torch/mace out of goldilocks-agent's own env; MLIP Playground shells out to `janus` (janus-core's own CLI) inside it, see tools/mlip_playground/ @@ -287,8 +287,8 @@ state while that happens, or a clear error if neither `GOLDILOCKS_AGENT_CORE_AUTOSTART` nor `GOLDILOCKS_CORE_PATH` is set. The published-package pipeline is currently a local tarball (`app/vendor/goldilocks-workbench-0.0.5.tgz`, rebuilt from `core/web` via -`npm run build:lib && npm pack`) rather than a real registry -- see issue #1 -for the GitHub Packages follow-up. +`npm run build:lib && npm pack`) rather than a real registry; publishing it from +goldilocks-core's CI is still to be done there. ## Usage diff --git a/app/src/App.tsx b/app/src/App.tsx index e611fd4..69c93cd 100755 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -117,7 +117,7 @@ const MODEL_GROUPS = [ }, ]; -// Flattens MODEL_GROUPS to look up a persisted selection by id (#4 -- reload +// Flattens MODEL_GROUPS to look up a persisted selection by id (cc5f0dc -- reload // restores it from storage, but the id alone can't tell us the display group, // so this recovers the full model object). Skips `disabled` entries: they // were never selectable in the first place, so a stale persisted id pointing @@ -137,7 +137,7 @@ function findModelById(id) { // in. Mirrors the live path's own filtering (drop `role: "tool"` entries and // content-less assistant turns -- design doc 12.3: chat never shows raw // tool args/results) but also recognizes a past `dft_download_bundle` result -// and re-inserts its `dft-bundle-ready` card (#5) -- the checkpointer has no +// and re-inserts its `dft-bundle-ready` card (cc5f0dc) -- the checkpointer has no // concept of that card, only the raw tool call/result pair, and OpenAI's // wire format puts the tool's *name* on the preceding assistant message's // `tool_calls[]`, not on the tool-role reply itself, so the id needs to be @@ -1711,7 +1711,7 @@ function coreWorkbenchStatusMessage(status: { status: string; detail: string | n if (status?.status === "error") { return status.detail ?? "Couldn't start goldilocks-core's backend -- see the server logs for details."; } - // (#3) The first-ever start downloads several hundred MB of models and + // (cc5f0dc) The first-ever start downloads several hundred MB of models and // pseudopotentials before goldilocks-core can even begin listening // (core_server.py's `_STARTUP_TIMEOUT` budgets 3 minutes for that; a // subsequent cold start with everything already cached is ~1s). This @@ -1851,7 +1851,7 @@ export default function App() { // (resolve_model()'s GOLDILOCKS_AGENT_MODEL env-var override always wins // server-side) -- defaulting to Claude there would misrepresent which // model actually answers. Elsewhere (non-shared), reload used to always - // reset this to Claude (#4) -- restore whatever was last picked instead, + // reset this to Claude (cc5f0dc) -- restore whatever was last picked instead, // same browserStorage()/STORAGE_KEYS pattern as activeChatId. const [selectedModel, setSelectedModel] = useState(() => SHARED_DEPLOYMENT @@ -2112,7 +2112,7 @@ export default function App() { loadConversationsOnce(); hydrateExperienceLevel(); } else { - // No conversation list to check against in shared mode (#9) -- the + // No conversation list to check against in shared mode (cc5f0dc) -- the // persisted id is this tab's own `sessionStorage` entry (never // another visitor's, browserStorage() already picked sessionStorage // for SHARED_DEPLOYMENT), so it's safe to trust directly. Synthesize @@ -2129,7 +2129,7 @@ export default function App() { } }, []); - // Restores which conversation was open before a reload (#9 -- previously + // Restores which conversation was open before a reload (cc5f0dc -- previously // nothing did, so a refresh always landed on a blank "no chat selected" // screen even though the conversation itself was still on the server). // Local-mode only: waits for `loadConversationsOnce()` above to actually @@ -2157,7 +2157,7 @@ export default function App() { }, [selectedModel]); // Persists `coreWorkspace`'s structure + calc settings on every real change - // (#5 -- previously a reload silently dropped all of it, since `coreWorkspace` + // (cc5f0dc -- previously a reload silently dropped all of it, since `coreWorkspace` // itself is recreated empty by the `useMemo` above). Deliberately excludes // `lastDownload`: it's a real `Blob`, not JSON-serializable, and the restore // effect below regenerates it fresh via `review.refreshArchive` instead of @@ -2177,10 +2177,10 @@ export default function App() { }, [coreWorkspace]); // Replays the persisted structure/settings back into `coreWorkspace` once, - // on mount (#5). A real re-run of inspect/review/generate against + // on mount (cc5f0dc). A real re-run of inspect/review/generate against // goldilocks-core's backend, not a cheap local restore -- there's no // "just set the state" action in goldilocks-workbench's `Workspace` - // interface, only real actions -- so this deliberately trades the #7 + // interface, only real actions -- so this deliberately trades c7bfc57's // no-silent-duplicate-real-execution principle for a seamless reload here // (explicit call, 2026-09-28, after weighing that tradeoff against the // cheaper alternatives). Best-effort: swallows failures so a structure that @@ -3330,7 +3330,7 @@ export default function App() { // // The streaming assistant bubble is found by its `streamId` in the live // `current.messages` on each delta, not rebuilt from a snapshot taken - // once at the start of the call (#6, 2026-09-28): a `tool_result`/ + // once at the start of the call (cc5f0dc, 2026-09-28): a `tool_result`/ // `client_tool_call` handler earlier in this same stream may already have // appended something (a `dft-bundle-ready` card, a confirmation card) via // `current.messages` -- reconstructing from a frozen snapshot on every diff --git a/src/goldilocks_agent/graph.py b/src/goldilocks_agent/graph.py index a8e13c4..ba54093 100644 --- a/src/goldilocks_agent/graph.py +++ b/src/goldilocks_agent/graph.py @@ -15,7 +15,7 @@ Because LangGraph replays a node's whole function body from the top on each resume, `call_tool` handles exactly *one* tool call per execution, never the whole batch on `state["messages"][-1].tool_calls` in a Python -loop (2026-09-28, #7): a loop with two-or-more interrupt-gated calls in +loop (2026-09-28, fixed in c7bfc57): a loop with two-or-more interrupt-gated calls in one execution replays every already-answered call's code -- including its real dispatch (`await fn(**call["args"])`), not just its `writer()` status -- every time a *later* call's interrupt resolves. For a real side effect @@ -304,7 +304,7 @@ def _find_next_tool_call(state: MessagesState) -> dict | None: matching ToolMessage in state yet, or None if every call on it is already answered. Scans backward for the nearest AIMessage rather than assuming `state["messages"][-1]` is it -- once `call_tool` starts - appending ToolMessages one at a time (#7), the tool-calling AIMessage + appending ToolMessages one at a time (c7bfc57), the tool-calling AIMessage is no longer the last element.""" messages = state["messages"] for i in range(len(messages) - 1, -1, -1): @@ -325,7 +325,7 @@ def _find_next_tool_call(state: MessagesState) -> dict | None: async def call_tool(state: MessagesState) -> dict: """Dispatch exactly one tool call -- the first unanswered one on the most recent AIMessage (`_find_next_tool_call`) -- never the whole - batch in one execution (see the module docstring's #7 note on why: + batch in one execution (see the module docstring's c7bfc57 note on why: LangGraph replays everything before an `interrupt()` on resume, so a Python loop handling several interrupt-gated calls in one execution re-runs earlier calls' real dispatch too, not just their status, @@ -436,7 +436,7 @@ def route_after_llm(state: MessagesState) -> str: def route_after_tool(state: MessagesState) -> str: """Loop back to `tool` while the most recent AIMessage still has an - unanswered tool call (#7 -- one call per `call_tool` execution, never + unanswered tool call (c7bfc57 -- one call per `call_tool` execution, never the whole batch); once every call has a ToolMessage, hand control back to `llm` to see the full result set.""" if _find_next_tool_call(state) is not None: