MIT license; devenv: dev appview (devenv up, ci:seed demo task)
- LICENSE (MIT; copyright line uses the name from the jj config) - devenv.nix: processes bridge, runner and appview on 127.0.0.1:8475-8477 (local launcher, bash actions, fixed dev credentials), scripts to build into .devenv/state/ci-dev, and the ci:seed task: a demo repo on a directory forge with a post-receive hook, so a push runs the real path and the demo run waits at a gate - README: development and license sections Bug de62253 filed for the workflow docs.
3 files changed,  +167, -7
A LICENSE
+21, -0
 1@@ -0,0 +1,21 @@
 2+MIT License
 3+
 4+Copyright (c) 2026 BT
 5+
 6+Permission is hereby granted, free of charge, to any person obtaining a copy
 7+of this software and associated documentation files (the "Software"), to deal
 8+in the Software without restriction, including without limitation the rights
 9+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+copies of the Software, and to permit persons to whom the Software is
11+furnished to do so, subject to the following conditions:
12+
13+The above copyright notice and this permission notice shall be included in all
14+copies or substantial portions of the Software.
15+
16+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+SOFTWARE.
M README.md
+16, -0
 1@@ -23,6 +23,22 @@ go test ./...
 2 nix build .#ci        # ./result/bin/ci
 3 ```
 4 
 5+To see the appview with something in it:
 6+
 7+```sh
 8+devenv up -d              # bridge, runner and appview on 127.0.0.1:8475-8477
 9+devenv tasks run ci:seed  # pushes a demo commit; its run waits at "Deploy demo?"
10+# open http://127.0.0.1:8477 and sign in with the password "dev"
11+devenv processes logs runner   # or: appview, bridge
12+devenv processes down
13+```
14+
15+`ci:seed` creates a demo repo on a directory "forge" (`.devenv/state/ci-dev`) with a post-receive hook for the bridge, so a push goes through the real path: hook, bridge, runner, a gated run. Run it again for another run. Actions use plain bash, not devenv; the dev credentials (`dev`, an agent token for `claude`) are fixed strings and only for this. The first start downloads Go modules, so give it a minute.
16+
17+## License
18+
19+MIT, see [LICENSE](LICENSE).
20+
21 ## Using the engine in another repo
22 
23 Add the flake as a devenv input and put the package on `PATH`:
M devenv.nix
+130, -7
  1@@ -1,8 +1,20 @@
  2-{ pkgs, ... }:
  3+{ pkgs, config, ... }:
  4 
  5-# Scaffold for a new project. devkit (imported in devenv.yaml) already provides
  6-# bug, secrets, local-code-review, jujutsu, and the customized interactive bash
  7-# environment, so don't repeat those here.
  8+# devkit (imported in devenv.yaml) already provides bug, secrets,
  9+# local-code-review, jujutsu, and the customized interactive bash environment,
 10+# so don't repeat those here.
 11+#
 12+# Seeing the appview in development:
 13+#
 14+#   devenv up -d            # bridge, runner and appview on 127.0.0.1:8475-8477
 15+#   devenv tasks run ci:seed   # a demo forge repo; pushing to it starts a gated run
 16+#   open http://127.0.0.1:8477   # password: dev
 17+#
 18+# Everything lives in .devenv/state/ci-dev (delete it to start over).
 19+let
 20+  dev = "${config.devenv.state}/ci-dev";
 21+  bin = "${dev}/bin/ci";
 22+in
 23 {
 24   languages.go.enable = true;
 25 
 26@@ -10,9 +22,120 @@
 27     pkgs.golangci-lint
 28     pkgs.just
 29     pkgs.jq
 30+    pkgs.git
 31+    pkgs.mkpasswd
 32+    pkgs.util-linux # flock
 33   ];
 34 
 35-  # enterShell = ''
 36-  #   project-specific setup; concatenated onto devkit's
 37-  # '';
 38+  # Builds the ci binary (and its ci-result alias) into the dev state dir.
 39+  # The three processes start together, so builds are serialized.
 40+  scripts.ci-dev-build.exec = ''
 41+    set -euo pipefail
 42+    mkdir -p ${dev}/bin
 43+    (
 44+      flock 9
 45+      cd "$DEVENV_ROOT"
 46+      go build -o ${dev}/bin/ci ./cmd/ci
 47+      ln -sf ci ${dev}/bin/ci-result
 48+    ) 9>${dev}/build.lock
 49+  '';
 50+
 51+  # Dev credentials: the appview password is "dev", the runner token and an
 52+  # agent token ("claude") are fixed strings. Never use these anywhere real.
 53+  scripts.ci-dev-secrets.exec = ''
 54+    set -euo pipefail
 55+    mkdir -p ${dev}
 56+    [ -f ${dev}/runner-token ] || echo dev-runner-token > ${dev}/runner-token
 57+    [ -f ${dev}/agent-tokens ] || echo "claude dev-agent-token-0123456789" > ${dev}/agent-tokens
 58+    [ -f ${dev}/password-hash ] || mkpasswd -m bcrypt -R 5 dev > ${dev}/password-hash
 59+  '';
 60+
 61+  processes.bridge.exec = ''
 62+    ci-dev-build
 63+    exec ${bin} bridge serve --listen 127.0.0.1:8475 \
 64+      --db ${dev}/bridge.db --spool ${dev}/spool --forge-host git.dev
 65+  '';
 66+
 67+  # Actions run with plain bash (no devenv) and as child processes: this is a
 68+  # UI playground, not the sandboxed production setup.
 69+  processes.runner.exec = ''
 70+    ci-dev-build
 71+    ci-dev-secrets
 72+    exec ${bin} runner serve --listen 127.0.0.1:8476 \
 73+      --state-dir ${dev}/runner \
 74+      --bridge ws://127.0.0.1:8475/events \
 75+      --knot git.dev --clone-url '${dev}/forge/{repo}.git' \
 76+      --launcher local --shell bash \
 77+      --token-file ${dev}/runner-token
 78+  '';
 79+
 80+  processes.appview.exec = ''
 81+    ci-dev-build
 82+    ci-dev-secrets
 83+    exec ${bin} appview serve --listen 127.0.0.1:8477 \
 84+      --runner http://127.0.0.1:8476 --runner-token-file ${dev}/runner-token \
 85+      --password-hash-file ${dev}/password-hash \
 86+      --agent-tokens-file ${dev}/agent-tokens \
 87+      --knot git.dev --secure-cookies=false
 88+  '';
 89+
 90+  # A demo repo on a "forge" that is a directory. Each run of this task commits
 91+  # to it and pushes; the repo's post-receive hook hands the push to the
 92+  # bridge, the runner starts the demo workflow, and the run waits at its
 93+  # "Deploy demo?" gate. Run it again for another run.
 94+  scripts.ci-dev-seed.exec = ''
 95+    set -euo pipefail
 96+    ci-dev-build
 97+    ci-dev-secrets
 98+    forge=${dev}/forge/demo.git
 99+    work=${dev}/work/demo
100+    if [ ! -d "$forge" ]; then
101+      git init -q --bare -b main "$forge"
102+      cat > "$forge/hooks/post-receive" <<HOOK
103+    #!/bin/sh
104+    exec ${bin} bridge notify --spool ${dev}/spool --server http://127.0.0.1:8475 --repo demo
105+    HOOK
106+      chmod +x "$forge/hooks/post-receive"
107+    fi
108+    if [ ! -d "$work/.git" ]; then
109+      mkdir -p "$work"
110+      git init -q -b main "$work"
111+      git -C "$work" remote add origin "$forge"
112+    fi
113+    mkdir -p "$work/.tangled/workflows"
114+    cat > "$work/.tangled/workflows/deploy.yml" <<'WF'
115+    # Demo workflow for the dev appview.
116+    when: [{ event: [push, manual], branch: main }]
117+    engine: devenv
118+    steps: [test, build, deploy]
119+    actions:
120+      test:
121+        command: |
122+          for i in 1 2 3; do echo "testing $i"; sleep 1; done
123+          echo "something noisy" >&2
124+          printf '%s\n' '{"suite":"demo","passed":3}' > "$CI_ARTIFACTS/report.json"
125+          ci-result ok --summary "3 tests passed" --attach "report=report.json"
126+      build:
127+        command: |
128+          echo "building $CI_SHA"
129+          echo built > "$CI_STATE/artifact"
130+      deploy:
131+        command: echo "deploying $(cat "$CI_STATE/artifact")"
132+        gate: { prompt: "Deploy demo?" }
133+      rollback:
134+        command: echo "rolling back $(cat "$CI_STATE/artifact")"
135+        manual: true
136+        style: danger
137+        label: Roll back
138+        gate: { prompt: "Roll the demo back?" }
139+    WF
140+    n=$(( $(cat "$work/counter" 2>/dev/null || echo 0) + 1 ))
141+    echo "$n" > "$work/counter"
142+    git -C "$work" add -A
143+    git -C "$work" -c user.name=dev -c [email protected] commit -q -m "demo change $n"
144+    git -C "$work" push -q origin main
145+    echo "pushed demo change $n; open http://127.0.0.1:8477 (password: dev)"
146+  '';
147+
148+  tasks."ci:seed".exec = "ci-dev-seed";
149 }