OXYGENHUB

Land Drops (#279) beside the settings pages (#278) d366306dae7d resolution

Resolutions: the push-protection refusal keeps Drops' branch (a drop has
no settings page) with #278's /settings/push-protection link; hub.js
keeps both handlers; the changelog keeps every Unreleased line.
Author
Andy McCall @andymccall <[email protected]>
via
agent agent:claude@andymccall <[email protected]> · keyless, under your grant for changes,refs,seal · key 45a6a4c1e0c5
When
· 2026-10-20 15:55 UTC
Signature
◈ sealed by agent:claude@andymccall
Hash
d366306dae7d601bf141448c5454b89299847bb45ce14f84a23ffd13906a1c24
Depends on
0ee5c3fb32eb 119fd17d213f 166f6e9bf4f1 a7aa76ac52d7 a7e6e8e6094c b5668108487f c7067c7bda56 d23464c0ac60 ea55fb62f919
Depended on by
a22e4aec2e28 4c77a1c8aad8
In bookmarks
api-unknown-fields diffusion-pin-bookmark draft-ready main proposal-list-fast
In tags
v0.6.1 v0.7.0 v0.8.0 v0.9.0 v0.9.1 v0.9.11 v0.9.15 v0.9.16 v0.9.19 v0.9.2 v0.9.20 v0.9.21 v0.9.22 v0.9.23 v0.9.24 v0.9.3 v0.9.4 v0.9.5 v0.9.6 v0.9.7 v0.9.8 v0.9.9
Edits
7 atoms, 0 tree operations, 3 files

Browse the tree at this change

modified CHANGELOG.md +2 −2
…
3 unchanged lines
4 4
5 5
## Unreleased
6 6
7 -
<<<<<<< oxy conflict 1 of 1, side 1 of 2: change 45e79740 "Repository settings: one page per setting under a grouped sidebar (#278)"
8 -
- **Repository settings are one page per setting (#278):** a repository's Settings was one long page; it is now a sidebar of grouped pages (General, Code, Flow, Integrations, Data, Danger zone) with the page shown beside it, the current one marked, each at its own address (`/{owner}/{name}/settings/push-protection`, `/settings/runs`, …; `/settings` is General). Pages a role cannot use are not listed and are not found. Every form keeps its address and now returns to its own page with what happened, so reloading does not send it again. Links elsewhere point at the new pages (the push protection refusal names `/settings/push-protection`), and a kept `/settings#section` link opens the page that has it. On a narrow screen the sidebar becomes a compact list above the page. An archived repository's settings open for its admins again, so they can bring it back.
7 +
- **Drops: snippets of files, each its own small repository (#279):** a drop is one or more files, as a gist is, kept as an Oxygen repository of its own at `/{owner}/drops/{id}` (a 24-hex-digit id), so every edit is a signed change with history and diffs, and `oxy clone` and `oxy push` take its address. Make one at `/drops/new` (title, description, files, public or secret), with `POST /api/v1/drops`, or with `oh drop create FILE… [--secret] [-d TEXT] [-t TITLE]`; the hub commits it as your delegate, as web edits are, so you delegate to the hub's key first. Your Drops page (`/drops`, under your profile and in the site band) lists and manages them; someone else's shows their public drops only. A secret drop is listed nowhere but readable by anyone with its address. Drops never appear among repositories (profiles, the dashboard, Explore, search, `GET /api/v1/repos`); Admin › Storage labels them. Only the owner edits a drop; the owner or a site administrator deletes it. Push protection covers drop pushes and what the web and the API commit. API: `GET`/`POST /drops`, `GET /users/{name}/drops`, `GET`/`PATCH`/`DELETE /drops/{id}`; `oh drop list|view|delete`. `drops` is now a reserved name, for owners and for repositories. Not yet: forks, stars, comments, embedding, and drops in backups.
8 +
- **Repository settings are one page per setting (#278):** a repository's Settings was one long page; it is now a sidebar of grouped pages (General, Code, Flow, Integrations, Data, Danger zone) with the page shown beside it, the current one marked, each at its own address (`/{owner}/{name}/settings/push-protection`, `/settings/runs`, …; `/settings` is General). Pages a role cannot use are not listed and are not found. Every form keeps its address and now returns to its own page with what happened, so reloading does not send it again. Links elsewhere point at the new pages (the push protection refusal names `/settings/push-protection`), and a kept `/settings#section` link opens the page that has it. On a narrow screen the sidebar becomes a compact list above the page. An archived repository's settings open for its admins again, so they can bring it back.
9 9
======= oxy conflict 1 of 1, side 2 of 2: change 8e9b151b "Drops: per-user gists, each a small hidden Oxygen repository (#279)"
10 10
- **Drops: snippets of files, each its own small repository (#279):** a drop is one or more files, as a gist is, kept as an Oxygen repository of its own at `/{owner}/drops/{id}` (a 24-hex-digit id), so every edit is a signed change with history and diffs, and `oxy clone` and `oxy push` take its address. Make one at `/drops/new` (title, description, files, public or secret), with `POST /api/v1/drops`, or with `oh drop create FILE… [--secret] [-d TEXT] [-t TITLE]`; the hub commits it as your delegate, as web edits are, so you delegate to the hub's key first. Your Drops page (`/drops`, under your profile and in the site band) lists and manages them; someone else's shows their public drops only. A secret drop is listed nowhere but readable by anyone with its address. Drops never appear among repositories (profiles, the dashboard, Explore, search, `GET /api/v1/repos`); Admin › Storage labels them. Only the owner edits a drop; the owner or a site administrator deletes it. Push protection covers drop pushes and what the web and the API commit. API: `GET`/`POST /drops`, `GET /users/{name}/drops`, `GET`/`PATCH`/`DELETE /drops/{id}`; `oh drop list|view|delete`. `drops` is now a reserved name, for owners and for repositories. Not yet: forks, stars, comments, embedding, and drops in backups.
11 11
>>>>>>> oxy conflict 1 of 1 end
…
259 unchanged lines
modified assets/hub.js +41 −41
…
915 unchanged lines
916 916
    sel.addEventListener("change", show);
917 917
    show();
918 918
  })();
919 -
<<<<<<< oxy conflict 1 of 1, side 1 of 2: change 45e79740 "Repository settings: one page per setting under a grouped sidebar (#278)"
920 -
921 -
  /* A repository's settings were one page with a section per #anchor;
922 -
     each is a page of its own now (oxygenhub#278). A kept link such as
923 -
     /settings#flow-priority opens the page that holds that section, when
924 -
     the viewer has it (its link is in the sidebar). */
925 -
  (function () {
926 -
    var nav = document.querySelector("[data-settings-anchors]");
927 -
    var hash = location.hash.slice(1);
928 -
    if (!nav || !/^[a-z-]+$/.test(hash)) return;
929 -
    var moved = { "history-scan": "push-protection", "flow-priority": "runs", "flow-supersede": "runs", "agent-gates": "gates" };
930 -
    var link = nav.querySelector('a[data-slug="' + (moved[hash] || hash) + '"]');
931 -
    if (link && link.getAttribute("aria-current") !== "page") location.replace(link.getAttribute("href") + "#" + hash);
932 -
  })();
933 -
======= oxy conflict 1 of 1, side 2 of 2: change 8e9b151b "Drops: per-user gists, each a small hidden Oxygen repository (#279)"
934 -
935 -
  /* Drops (oxygenhub#279): add and remove file rows on the drop form.
936 -
     Without this file the form has its rows and one empty one; more come
937 -
     by saving and editing again. */
938 -
  (function () {
939 -
    var list = document.querySelector("[data-drop-files]");
940 -
    var add = document.querySelector("[data-drop-add]");
941 -
    if (!list || !add) return;
942 -
    function wire(row) {
943 -
      var rm = row.querySelector("[data-drop-remove]");
944 -
      if (!rm) return;
945 -
      rm.hidden = false;
946 -
      rm.addEventListener("click", function () {
947 -
        if (list.querySelectorAll("[data-drop-file]").length > 1) row.remove();
948 -
        else row.querySelectorAll("input, textarea").forEach(function (f) { f.value = ""; });
949 -
      });
950 -
    }
951 -
    list.querySelectorAll("[data-drop-file]").forEach(wire);
952 -
    add.hidden = false;
953 -
    add.addEventListener("click", function () {
954 -
      var rows = list.querySelectorAll("[data-drop-file]");
955 -
      var row = rows[rows.length - 1].cloneNode(true);
956 -
      row.querySelectorAll("input, textarea").forEach(function (f) { f.value = ""; f.textContent = ""; });
957 -
      list.appendChild(row);
958 -
      wire(row);
959 -
      row.querySelector("input").focus();
919 +
920 +
  /* Drops (oxygenhub#279): add and remove file rows on the drop form.
921 +
     Without this file the form has its rows and one empty one; more come
922 +
     by saving and editing again. */
923 +
  (function () {
924 +
    var list = document.querySelector("[data-drop-files]");
925 +
    var add = document.querySelector("[data-drop-add]");
926 +
    if (!list || !add) return;
927 +
    function wire(row) {
928 +
      var rm = row.querySelector("[data-drop-remove]");
929 +
      if (!rm) return;
930 +
      rm.hidden = false;
931 +
      rm.addEventListener("click", function () {
932 +
        if (list.querySelectorAll("[data-drop-file]").length > 1) row.remove();
933 +
        else row.querySelectorAll("input, textarea").forEach(function (f) { f.value = ""; });
934 +
      });
935 +
    }
936 +
    list.querySelectorAll("[data-drop-file]").forEach(wire);
937 +
    add.hidden = false;
938 +
    add.addEventListener("click", function () {
939 +
      var rows = list.querySelectorAll("[data-drop-file]");
940 +
      var row = rows[rows.length - 1].cloneNode(true);
941 +
      row.querySelectorAll("input, textarea").forEach(function (f) { f.value = ""; f.textContent = ""; });
942 +
      list.appendChild(row);
943 +
      wire(row);
944 +
      row.querySelector("input").focus();
945 +
    });
946 +
  })();
947 +
948 +
  /* A repository's settings were one page with a section per #anchor;
949 +
     each is a page of its own now (oxygenhub#278). A kept link such as
950 +
     /settings#flow-priority opens the page that holds that section, when
951 +
     the viewer has it (its link is in the sidebar). */
952 +
  (function () {
953 +
    var nav = document.querySelector("[data-settings-anchors]");
954 +
    var hash = location.hash.slice(1);
955 +
    if (!nav || !/^[a-z-]+$/.test(hash)) return;
956 +
    var moved = { "history-scan": "push-protection", "flow-priority": "runs", "flow-supersede": "runs", "agent-gates": "gates" };
957 +
    var link = nav.querySelector('a[data-slug="' + (moved[hash] || hash) + '"]');
958 +
    if (link && link.getAttribute("aria-current") !== "page") location.replace(link.getAttribute("href") + "#" + hash);
959 +
  })();
960 960
    });
961 961
  })();
962 962
>>>>>>> oxy conflict 1 of 1 end
…
1 unchanged lines
modified crates/hub/src/push_protection.rs +1 −2
…
501 unchanged lines
502 502
    } else {
503 503
        let _ = writeln!(
504 504
            out,
505 -
            "  - If {is} not a secret (an example, a test value), a maintainer can allow its fingerprint, with a reason, at {settings}#push-protection; then push again."
505 +
            "  - If {is} not a secret (an example, a test value), a maintainer can allow its fingerprint, with a reason, at {settings}/push-protection; then push again."
506 506
        );
507 507
    }
508 508
    if !via.is_empty() && settings.is_empty() {
…
2 unchanged lines
511 511
            "\nThis push came through an agent's token ({via}). As an agent: remove the value from the change (`oxy amend`) and push again; do not try to get around the refusal. If you were asked to commit this value, stop and tell the person you work for."
512 512
        );
513 513
    } else if !via.is_empty() {
514 -
<<<<<<< oxy conflict 1 of 1, side 1 of 2: deleted by change 8e9b151b "Drops: per-user gists, each a small hidden Oxygen repository (#279)"
515 514
-------
516 515
        out,
517 516
======= oxy conflict 1 of 1, side 2 of 2: change 45e79740 "Repository settings: one page per setting under a grouped sidebar (#278)"
…
80 unchanged lines