Skip to content

Delivery board reads the package repository: an instance shows another repository numbers and says nothing #1250

Closed

Description

@Birfy

Owner: Dev4
(Proposed by Chief on 2026-10-26 from the owner's correction: "固定写在包里肯定不行". Triage: confirm or move.)

Goal (one observable behavior)

The delivery board reads the repository this instance actually works in, and says which one it read and where that name came from. Today it cannot: core/src/host/delivery-board.js:745-748

const repo = (() => { try { return rt.repoSlug?.() ?? repoSlug() } catch { return repoSlug() } })()

repoSlug() (core/src/host/update.js:163) parses the shipped package's repository.url, and nothing in the tree provides rt.repoSlug (grep: no rt.repoSlug = anywhere). So every instance shows ThinkFlowLab/FlyBot no matter what it is connected to — an instance set up on someone's own fork shows our numbers, and a fresh instance with no token shows our numbers too.

Resolution order (this is the whole design)

First one that answers wins, and the view must carry which one answered:

  1. Instance setting — delivery.repo: "owner/name" in the instance config (the same place other instance settings live). Explicit beats inferred.
  2. The workspace's git remote — origin of the folder the work happens in (git remote get-url origin, accepting https://github.com/o/r(.git) and git@github.com:o/r.git). This is what "the repo this instance works in" means in practice.
  3. Package repository.url — last resort only, and only so an untouched instance still shows something. It must be labelled as a default, not presented as fact.

If none answers: the view fails with no repository — set delivery.repo. It must not request /repos/null.

Acceptance criteria

  1. With delivery.repo = "acme/widget", board and probe both read acme/widget, and the header shows acme/widget with source config.
  2. With no setting and a workspace whose origin is https://github.com/acme/widget.git, both read acme/widget, source remote. A table-driven test covers the git@github.com:acme/widget.git and scp-less https://github.com/acme/widget forms.
  3. With neither, the resolved name is the package one and the header says source package (a warn line records that it is a default). No silent fallback in the logs.
  4. With neither and a package whose repository is absent, the view fails with the message above and the probe exits non-zero — assert that no request was made at all.
  5. Changing the setting re-resolves: the cache is keyed by repo, so the previous repo's snapshot is never served for the new name (assert the second read is loading, not the old ready with old columns).
  6. A repo the saved GITHUB_TOKEN cannot read fails with a message naming the repo and the endpoint (the #1211/#1232 failure shape already carries endpoint/status — reuse it, do not invent a second shape).
  7. npm run verify green.

The knife: delete the config branch → (1) goes red; delete the remote branch → (2) goes red. Keep both in the same test file so one cannot rot silently.

Where it sits (file:line pointers)

  • core/src/host/delivery-board.js:745-748 — install()'s repo resolution (the bug).
  • core/src/host/update.js:163 — repoSlug(); keep it for what it is good at (where this package's releases live), do not repurpose it.
  • core/scripts/delivery-probe.mjs:18 — same order, so the CLI and the button agree; --repo stays as the explicit override.
  • core/src/client/delivery-view.js:276 — DeliveryHeader, which already receives repo; add the source label here.
  • docs/subsystems/core-host.md — the arch-docs owner page for core/src/host/; add the setting and the order.

Out of scope / do not touch

  • Release checks (core/src/host/update.js, dsh-update.js) keep using the package URL: "where releases of the installed package live" is genuinely a package fact, not an instance choice. Do not unify the two.
  • No settings UI beyond showing the resolved name and source; editing delivery.repo stays a config/secret action in this issue.
  • No change to which endpoints the board reads, to the cache TTL, or to the columns.
  • Do not add a second way to name the repo (no env var and config and CLI all matching different things: config, then remote, then package, and CLI --repo overrides).

Why it bites

An instance this team does not own was showing this repository's numbers, and nothing on the page said whose repository it was. The owner's words: 固定写在包里肯定不行. A default is acceptable only while it is visible as a default.

Activity

  1. Birfy commented on Oct 26, 2026

    @Birfy
    CollaboratorAuthor

    Triage — every pointer resolves at 7be5658a; Owner: Dev4 confirmed. Five forks decided so this can be implemented without a round trip

    Checked: delivery-board.js:744-748 (install() + the rt.repoSlug?.() ?? repoSlug() IIFE), update.js:162-163 (repoSlug() off PKG.repository), delivery-probe.mjs:18, delivery-view.js:276 (DeliveryHeader({ view, snapshot, repo = null, … })). No duplicate: nothing open covers this; #1211 was the failure-shape issue and is closed.

    1. Level 1 has a home already — rt.config. Do not invent a settings store. parts.js:140 destructures { ctx, config, state } from rt and reads config.checkRatio at :213, and this host's own profile sets it today: /data/dsh/profiles/web/cordis.patch.yml has - id: bot, name: ds-bot, config: { checkRatio: 0.45 }. So delivery.repo is one more key on that same plugin entry (config.deliveryRepo), read via rt.config — no new tool, no new UI, no new file. The wrong alternative is a secret: rt.secretValue (secrets.js:607) holds scoped key values and the list renders them as keys, which a repo name is not.
    2. repo cannot stay a constant. It is baked into base once (delivery-board.js:258) and reused by every endpoint, every error message and probe() (:741). So install() must resolve { repo, source } per rt.deliveryBoard() call and keep Map<repo, board> — a different resolved repo gets that repo's own board instance. That makes criterion 5 structural ("the new repo's first read is loading", not a cache flush bug). Do not push a function into createDeliveryBoard({ repo }); base is built once from it.
    3. Level 2 reads .git/config, it does not spawn git. There is no subprocess anywhere in core/src/host (grep execFile: 0 hits) and the host may run where git is not on PATH. Parse the [remote "origin"] url = value out of <folder>/.git/config (follow gitdir: when .git is a file), synchronously and testably. The folder is the instance's team workspace — teamFolder() (folders.js:25, ctx.workspaceRegistry.get(state.workspaceId)?.path), not a Bot's workDir (workDirOf, folders.js:24): the board is host-level, one host shows one repo, and using a Bot's folder would make the answer depend on which Bot happened to be named. Do not union disagreeing remotes; the header's source label is what makes the choice honest.
    4. The probe cannot read instance config, so criterion 1 as written cannot hold for it. delivery-probe.mjs is a standalone script with no rt and no state. Restate its order as --repo → --workspace <dir> (new flag, default cwd) → package, and put the resolver in one module both call sites import (e.g. core/src/host/delivery-repo.js, resolveRepo({ setting, workspace, packageSlug })), so the board and the CLI cannot drift — and the knife (delete config branch / delete remote branch) can be one table-driven test against the resolver, not two copies.
    5. The /repos/null guard must sit before the board is built. base is interpolated at delivery-board.js:258 and probeGitHub:211, so a null repo becomes /repos/null unless resolution refuses first. Criterion 4's "assert no request" has to count every fetch on the whole path, including the first rt.deliveryBoard() read, and must see zero.

    Criterion 6 is nearly free: hintFor(error, { repo }) already receives the repo (delivery-view.js:299) and the #1211/#1232 error shape already carries endpoint/status. What is missing is only that the 404 for an unreadable repo names the repo rather than the endpoint alone.

    Labels: enhancement, area:ui, agent-ready (the visible surface is the header's repo + source line). Out-of-scope section is right: update.js/dsh-update.js keep the package URL — that is a package fact, not an instance choice.

  2. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    area:uiWeb/desktop UI and copy
    enhancementNew feature or request
    on Oct 26, 2026
  3. Birfy commented on Oct 26, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Dev4
    Claimed #1250 — branch dev/feat/1250-delivery-repo — first step: the resolver both the host and the probe import (core/src/host/delivery-repo.js): delivery.repo → the team workspace git origin → the package as a labelled default.

  4. added
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 26, 2026
  5. removed
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:uiWeb/desktop UI and copyenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions