autonomous-sdlc-harness 0.4.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/commands/init.js +113 -16
  2. package/dist/commands/init.js.map +1 -1
  3. package/dist/config/check.js +28 -6
  4. package/dist/config/check.js.map +1 -1
  5. package/dist/config/model.js +64 -5
  6. package/dist/config/model.js.map +1 -1
  7. package/dist/core/pluginIdentity.js +2 -0
  8. package/dist/core/pluginIdentity.js.map +1 -1
  9. package/dist/core/writer.js +10 -5
  10. package/dist/core/writer.js.map +1 -1
  11. package/dist/core/yamlScalar.js +14 -0
  12. package/dist/core/yamlScalar.js.map +1 -0
  13. package/dist/doctor/checks.js +454 -23
  14. package/dist/doctor/checks.js.map +1 -1
  15. package/dist/generators/githubWorkflows.js +125 -20
  16. package/dist/generators/githubWorkflows.js.map +1 -1
  17. package/dist/generators/repoRoot.js +17 -8
  18. package/dist/generators/repoRoot.js.map +1 -1
  19. package/dist/remote/githubActions.js +110 -7
  20. package/dist/remote/githubActions.js.map +1 -1
  21. package/dist/retrieval/pythonBackend.js +114 -0
  22. package/dist/retrieval/pythonBackend.js.map +1 -0
  23. package/dist/retrieval/setup.js +8 -0
  24. package/dist/retrieval/setup.js.map +1 -1
  25. package/package.json +1 -1
  26. package/templates/README.md +1 -1
  27. package/templates/github/workflows/harness-control.yml +184 -0
  28. package/templates/github/workflows/harness-resume.yml +9 -0
  29. package/templates/github/workflows/harness-run.yml +127 -12
  30. package/templates/github/workflows/harness-trigger.yml +144 -0
  31. package/templates/repo/gitignore +5 -0
  32. package/templates/scripts/README.md +1 -1
  33. package/templates/scripts/autonomous-watcher.sh +121 -249
  34. package/templates/scripts/create-worktree.sh +52 -10
  35. package/templates/scripts/docs-search-server.sh +88 -17
  36. package/templates/scripts/lib/harness-run-lib.sh +614 -14
  37. package/templates/scripts/remote-run.sh +3684 -144
  38. package/templates/scripts/scratch-run.sh +54 -73
  39. package/templates/state-dir/README-root.md +1 -1
  40. package/templates/state-dir/scratch/README.md +4 -2
  41. package/templates/state-dir/user_reviews/README.md +2 -2
@@ -14,6 +14,10 @@
14
14
  * runtime is a warning rather than an `npm` call, so a test that forgets to plant one stays offline.
15
15
  * - **A failed step is a warning.** The wiring the plan wrote stays valid; `doctor` fails until a
16
16
  * re-run completes the step.
17
+ * - **The Python backend gets no install and no download here, and the TypeScript setup still runs.**
18
+ * Its package, weights and Postgres are provisioned by hand; the TypeScript runtime and models are
19
+ * kept so switching `docs.retrievalBackend` back costs nothing. With `python` selected, one note
20
+ * says both.
17
21
  *
18
22
  * Both steps put machine state outside the repository, not through the write engine
19
23
  * (`cli/src/core/writer.ts` header). `setUpRuntime` writes the runtime itself, through `npm`;
@@ -25,6 +29,7 @@ import { mkdirSync } from 'node:fs';
25
29
  import { join } from 'node:path';
26
30
  import { ownManifestString } from '../core/paths.js';
27
31
  import { modelFilesPresent, RETRIEVAL_STUB_ENV, stubModelsSelected } from './models.js';
32
+ import { PYTHON_RETRIEVAL_COMMAND } from './pythonBackend.js';
28
33
  import { retrievalCliEntry, retrievalModelCacheDir, retrievalPeers, retrievalRuntimeDir, retrievalRuntimeState, RUNTIME_CLI_RELATIVE, } from './runtime.js';
29
34
  /** Both child processes may take minutes on a cold cache. */
30
35
  const STEP_TIMEOUT_MS = 900000;
@@ -116,6 +121,9 @@ export function setUpRetrieval(options) {
116
121
  const warnings = [];
117
122
  setUpRuntime(options.dryRun, notes, warnings);
118
123
  setUpModels(options.dryRun, notes, warnings);
124
+ if (options.backend === 'python') {
125
+ notes.push(`docs.retrievalBackend is python: init installed and downloaded nothing for it — the \`${PYTHON_RETRIEVAL_COMMAND}\` package, its weights and its Postgres are set up by hand (docs/retrieval.md → Turning on the Python backend); the TypeScript runtime and models above are kept so switching back costs nothing`);
126
+ }
119
127
  return { notes, warnings };
120
128
  }
121
129
  //# sourceMappingURL=setup.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/retrieval/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,cAAc,CAAC;AAEtB,6DAA6D;AAC7D,MAAM,eAAe,GAAG,MAAM,CAAC;AAE/B,MAAM,MAAM,GACV,yGAAyG,CAAC;AAE5G,uGAAuG;AACvG,SAAS,cAAc,CAAC,IAAY,EAAE,OAA0B,EAAE,KAAc;IAC9E,MAAM,OAAO,GAAG,KAA2G,CAAC;IAC5H,MAAM,MAAM,GACV,OAAO,OAAO,EAAE,MAAM,KAAK,QAAQ;QACjC,CAAC,CAAC,eAAe,OAAO,CAAC,MAAM,EAAE;QACjC,CAAC,CAAC,OAAO,EAAE,MAAM;YACf,CAAC,CAAC,aAAa,OAAO,CAAC,MAAM,EAAE;YAC/B,CAAC,CAAC,YAAY,OAAO,EAAE,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;IACtD,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAG,MAAM;SACpB,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;SAC7B,GAAG,EAAE,CAAC;IACT,OAAO,kBAAkB,IAAI,cAAc,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,MAAM,GACjF,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,QAAQ,EAC7C,sDAAsD,MAAM,EAAE,CAAC;AACjE,CAAC;AAED,SAAS,OAAO,CAAC,IAAY,EAAE,OAAuC,EAAE,QAAkB;IACxF,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC;IAChC,IAAI,CAAC;QACH,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC;IAC9F,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,MAAe,EAAE,KAAe,EAAE,QAAkB;IACxE,MAAM,UAAU,GAAG,mBAAmB,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,qBAAqB,EAAE,CAAC;IACtC,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,qDAAqD,KAAK,CAAC,OAAO,IAAI,SAAS,OAAO,UAAU,EAAE,CAAC,CAAC;QAC/G,OAAO;IACT,CAAC;IACD,MAAM,OAAO,GAA0B;QACrC,KAAK;QACL,SAAS;QACT,UAAU;QACV,UAAU;QACV,YAAY;QACZ,WAAW;QACX,GAAG,iBAAiB,CAAC,MAAM,CAAC,IAAI,iBAAiB,CAAC,SAAS,CAAC,EAAE;QAC9D,GAAG,cAAc,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;KAChE,CAAC;IACF,IAAI,MAAM,EAAE,CAAC;QACX,KAAK,CAAC,IAAI,CAAC,0DAA0D,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5F,OAAO;IACT,CAAC;IACD,IAAI,kBAAkB,EAAE,EAAE,CAAC;QACzB,QAAQ,CAAC,IAAI,CACX,8CAA8C,UAAU,qBAAqB,kBAAkB,kCAAkC,kBAAkB,QAAQ,MAAM,EAAE,CACpK,CAAC;QACF,OAAO;IACT,CAAC;IACD,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,OAAO,CAAC,iBAAiB,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,WAAW,CAAC,MAAe,EAAE,KAAe,EAAE,QAAkB;IACvE,MAAM,QAAQ,GAAG,sBAAsB,EAAE,CAAC;IAC1C,IAAI,kBAAkB,EAAE,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,uCAAuC,kBAAkB,wBAAwB,CAAC,CAAC;QAC9F,OAAO;IACT,CAAC;IACD,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,2CAA2C,QAAQ,EAAE,CAAC,CAAC;QAClE,OAAO;IACT,CAAC;IACD,4GAA4G;IAC5G,MAAM,KAAK,GAAG,iBAAiB,EAAE,EAAE,KAAK,CAAC;IACzC,MAAM,OAAO,GAA0B;QACrC,OAAO,CAAC,QAAQ;QAChB,KAAK,IAAI,IAAI,CAAC,mBAAmB,EAAE,EAAE,oBAAoB,CAAC;QAC1D,MAAM;QACN,cAAc;KACf,CAAC;IACF,IAAI,MAAM,EAAE,CAAC;QACX,KAAK,CAAC,IAAI,CAAC,kDAAkD,QAAQ,iBAAiB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC7G,OAAO;IACT,CAAC;IACD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,QAAQ,CAAC,IAAI,CACX,mHAAmH,mBAAmB,EAAE,sBAAsB,MAAM,EAAE,CACvK,CAAC;QACF,OAAO;IACT,CAAC;IACD,OAAO,CAAC,gBAAgB,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC/C,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,cAAc,CAAC,OAA4B;IACzD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC9C,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC7C,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/retrieval/setup.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autonomous-sdlc-harness",
3
- "version": "0.4.2",
3
+ "version": "0.6.0",
4
4
  "description": "The outer loop of the autonomous SDLC harness: init, doctor, config and daemon management for the harness Claude Code plugin, plus opt-in local docs retrieval.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "firu-daniel",
@@ -9,7 +9,7 @@ The generator templates `init` copies into an adopted repository. Nothing here i
9
9
  | `githooks/` | the configured `githooksDir` |
10
10
  | `scripts/` | the configured `scriptsDir` |
11
11
  | `state-dir/` | the configured `stateDir` |
12
- | `github/` | `.github/` in the adopter's repo — `workflows/harness-run.yml` and `workflows/harness-resume.yml`, written only when `execution.target` is `github-actions` |
12
+ | `github/` | `.github/` in the adopter's repo — `workflows/harness-run.yml` and `workflows/harness-resume.yml`, written only when `execution.target` is `github-actions`, and `workflows/harness-trigger.yml` and `workflows/harness-control.yml`, written only when `forge` is also `github` |
13
13
 
14
14
  **Naming rule for the whole tree: a template whose adopter-side name begins with a dot is stored here without the dot.** `repo/gitignore` is written as `.gitignore`, `repo/mcp.json` as `.mcp.json`, and the `claude/` subtree lands at `.claude/` and the `github/` subtree at `.github/`; `init` adds the dot when it writes. The rule is load-bearing twice over — a real `.gitignore` inside this repository would be honoured by git and could silently exclude files that are meant to ship, and npm rewrites a packaged `.gitignore` to `.npmignore` on publish, so a dot-named template does not survive the wire.
15
15
 
@@ -0,0 +1,184 @@
1
+ # harness-control.yml — acts on an autonomous-sdlc-harness command comment
2
+ # (`@sdlc-harness answer|pause|resume|stop|clear`) on an issue or pull request,
3
+ # and turns a pull-request review requesting changes into the next user-review
4
+ # round.
5
+ #
6
+ # WHO WRITES IT. `autonomous-sdlc-harness init`, only when harness.config.json's
7
+ # `forge` is `github` and its `execution.target` is `github-actions`. Written
8
+ # create-if-absent: from then on it is yours to tune, and a re-run of `init`
9
+ # keeps your copy. `init --upgrade-workflows` does NOT re-render it: it carries
10
+ # no version pin and calls the scripts on the default branch, so it shares
11
+ # their re-run contract — `init --force` replaces it after a `.bak`. It is a
12
+ # file of its own, apart from harness-trigger.yml, so you can disable comment
13
+ # control alone: disabling a workflow disables every event in it.
14
+ #
15
+ # WHAT IT DOES. One job runs `remote-run.sh control`, which does everything
16
+ # else: it authorises the actor, finds the run's branch, carries out the
17
+ # command or places the review round, and replies. Every decision lives in that
18
+ # script; this file carries only the events, the permissions, a prefilter and
19
+ # the concurrency group.
20
+ # The `run-name` below is not harness-run.yml's `harness <action> <branch>`
21
+ # title, so no run lookup `remote-run.sh` makes by that title can match it.
22
+ #
23
+ # THE EVENTS.
24
+ # issue_comment, type `created` only: a command is a new comment. GitHub
25
+ # raises it for a comment on an issue and on a pull request alike.
26
+ # pull_request_review, type `submitted` only: the round is built from the
27
+ # review event, which carries the review body and identifies its inline
28
+ # comments (docs/github-integration-research.md, C1).
29
+ # Never pull_request_review_comment: one review raises one such event per
30
+ # inline comment, so listening to it would start one job per comment for a
31
+ # round the review event already covers (C1).
32
+ #
33
+ # THE PREFILTER. The job's `if:` runs it only for a comment that contains
34
+ # `@sdlc-harness` and not `<!-- sdlc-harness` (the hidden line every harness
35
+ # reply carries, so a reply quoting a command is never one), or for a review
36
+ # whose state is `changes_requested` on a pull request from this repository.
37
+ # Every other comment or review yields a skipped job, which runs no runner and
38
+ # bills nothing (docs/remote-execution.md, `### Verified in Gate 12 round 2`).
39
+ # It is a saving, not the authority: `control` re-checks every rule, so an
40
+ # edited copy of this filter cannot bypass one. That `contains()` compares
41
+ # case-insensitively is GitHub's documented behaviour, not verified here; a
42
+ # mixed-case handle the filter missed would only go unanswered, never obeyed.
43
+ #
44
+ # THE PERMISSIONS, declared because the default token may be read-only
45
+ # (docs/github-integration-research.md, T2):
46
+ # contents: write push a review round
47
+ # actions: write dispatch, pause, stop and cancel runs, and read
48
+ # runs and their artifacts
49
+ # issues: write reply on the issue or pull request, and label it
50
+ # pull-requests: write read the pull request, its reviews and comments,
51
+ # and label it
52
+ # The job reads no repository secret: it needs only its own token, so the
53
+ # credential secrets never reach the job that reads untrusted comment text.
54
+ # Every reply is posted as github-actions[bot] and starts no workflow (S3).
55
+ #
56
+ # FORK PULL REQUESTS. Never `pull_request_target`, which would run with this
57
+ # repository's write token on a fork's event (C2).
58
+ # A `pull_request_review` job runs this file as the pull request's merge
59
+ # commit carries it (docs/github-integration-research.md, C2), so the `if:`
60
+ # that skips a fork's review is the fork's own copy: a fork that edits it is
61
+ # not skipped, and can name any `runs-on:` label, a self-hosted runner's
62
+ # included. Such a job gets a read-only token and no secret (C2), and fork
63
+ # approval policies apply to it.
64
+ # A comment on a fork's pull request runs with a write token, and `control`
65
+ # refuses it.
66
+ # The checkout is always the default branch, never the pull request's merge
67
+ # commit a `pull_request_review` job checks out by default, so the script that
68
+ # decides is always the default branch's; what a pull request's head can change
69
+ # is this file's own definition for its own review events.
70
+ #
71
+ # THE CONCURRENCY GROUP SERIALIZES REVIEW JOBS PER BRANCH, NEVER COMMENT JOBS.
72
+ # * A comment job's group is its own run id, so it never waits and is never
73
+ # replaced: a group keeps at most one pending job and cancels an earlier
74
+ # pending one, which would drop a command.
75
+ # * Review jobs on one head branch share `harness-review-<branch>` and run one
76
+ # at a time, so two reviews submitted together give one round, not two
77
+ # pushes racing for the same round number.
78
+ # * A pending review job replaced by a newer one loses nothing: every job in
79
+ # the group collects cumulatively, every review requesting changes, every
80
+ # other review carrying a summary and every inline comment no earlier round
81
+ # recorded, and the round's comment names
82
+ # every reviewer it took.
83
+ # * The name `harness-review-<branch>` is reserved for every job that places a
84
+ # review round on that branch; any other workflow's job that does so joins
85
+ # the group under exactly that name, and both files' headers declare it.
86
+ # harness-run.yml's `collect` job is the one such job today (DECLARED
87
+ # MIRRORS below).
88
+ #
89
+
90
+ # TWO RULES EVERY EDIT KEEPS.
91
+ # * Event text and variables reach a shell line only through `env:` and the
92
+ # event file, never through a GitHub expression inside `run:` — a comment
93
+ # body is written by whoever posted it (script injection).
94
+ # * Every GitHub expression has a space after its opening braces. `init`
95
+ # renders its workflows with the CLI's double-brace token renderer, which
96
+ # reads a letter straight after the braces as a token; this file carries
97
+ # no token.
98
+ #
99
+ # DECLARED MIRRORS — a rename on either side is an edit to both:
100
+ # cli/src/remote/githubActions.ts WORKFLOW_CONTROL_FILE, COMMAND_HANDLE
101
+ # (`@sdlc-harness`), COMMENT_MARKER
102
+ # (`<!-- sdlc-harness`), REVIEW_ROUND_STATE
103
+ # (`changes_requested`), HARNESS_RUNNER,
104
+ # HARNESS_REMOTE_STOP,
105
+ # HARNESS_TRIGGER_ALLOWED_BOTS
106
+ # cli/src/config/model.ts DEFAULTS.scriptsDir
107
+ # harness-run.yml the concurrency group
108
+ # `harness-review-<branch>`, held by its
109
+ # `collect` job
110
+ #
111
+ # ACTION PINS.
112
+ # actions/checkout@v5
113
+ # The lowest major whose own action.yml declares `runs.using: node24`, per a
114
+ # maintainer's lookups of its action.yml and release notes on
115
+ # 2026-09-29T08:21Z (UTC). That major's release notes require Actions Runner
116
+ # 2.327.1 or newer; a GitHub-hosted runner already has it, and a self-hosted
117
+ # one must run it (docs/remote-execution.md, section 8). A major tag, not a
118
+ # commit sha: it is GitHub's own `actions/` organisation, the owner of the
119
+ # runner and GITHUB_TOKEN this job already trusts, and a major tag takes the
120
+ # action's own patch and security releases where a sha would freeze your copy.
121
+ # Pin a sha in your copy if you want one.
122
+
123
+ name: harness-control
124
+ run-name: harness control ${{ github.event.issue.number || github.event.pull_request.number }}
125
+
126
+ on:
127
+ issue_comment:
128
+ types: [created]
129
+ pull_request_review:
130
+ types: [submitted]
131
+
132
+ permissions:
133
+ contents: write
134
+ actions: write
135
+ issues: write
136
+ pull-requests: write
137
+
138
+ defaults:
139
+ run:
140
+ shell: bash
141
+
142
+ jobs:
143
+ control:
144
+ if: (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@sdlc-harness') && !contains(github.event.comment.body, '<!-- sdlc-harness')) || (github.event_name == 'pull_request_review' && github.event.review.state == 'changes_requested' && github.event.pull_request.head.repo.full_name == github.repository)
145
+ runs-on: ${{ vars.HARNESS_RUNNER || 'ubuntu-latest' }}
146
+ concurrency:
147
+ group: ${{ github.event_name == 'pull_request_review' && format('harness-review-{0}', github.event.pull_request.head.ref) || format('harness-control-{0}', github.run_id) }}
148
+ cancel-in-progress: false
149
+ env:
150
+ GH_TOKEN: ${{ github.token }}
151
+ HARNESS_REMOTE_STOP: ${{ vars.HARNESS_REMOTE_STOP }}
152
+ HARNESS_REMOTE_SLUG: ${{ github.repository }}
153
+ HARNESS_TRIGGER_ALLOWED_BOTS: ${{ vars.HARNESS_TRIGGER_ALLOWED_BOTS }}
154
+ steps:
155
+ - name: Check out the default branch
156
+ uses: actions/checkout@v5
157
+ with:
158
+ ref: ${{ github.event.repository.default_branch }}
159
+ token: ${{ github.token }}
160
+ fetch-depth: 0
161
+
162
+ - name: Check for jq and gh
163
+ run: |
164
+ missing=""
165
+ for tool in jq gh; do
166
+ command -v "$tool" >/dev/null 2>&1 || missing="$missing $tool"
167
+ done
168
+ if [ -n "$missing" ]; then
169
+ echo "::error::this runner lacks:$missing. Install them on the self-hosted runner, or use a GitHub-hosted one."
170
+ exit 1
171
+ fi
172
+
173
+ - name: Read the configuration
174
+ run: |
175
+ scripts_dir=$(jq -r '.scriptsDir // "scripts"' harness.config.json)
176
+ echo "SCRIPTS_DIR=${scripts_dir%/}" >> "$GITHUB_ENV"
177
+
178
+ - name: Set the git identity
179
+ run: |
180
+ git config user.name "github-actions[bot]"
181
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
182
+
183
+ - name: Act on the comment or review
184
+ run: bash "$SCRIPTS_DIR/remote-run.sh" control
@@ -4,6 +4,8 @@
4
4
  # WHO WRITES IT. `autonomous-sdlc-harness init`, only when harness.config.json's
5
5
  # `execution.target` is `github-actions`. Written create-if-absent: from then on
6
6
  # it is yours to tune, and a re-run of `init` keeps your copy.
7
+ # `init --upgrade-workflows` re-renders it together with the run workflow, after
8
+ # a `.bak`, carrying its `- cron:` lines.
7
9
  #
8
10
  # WHY A POLLER. A `schedule` trigger is a recurring cron read from the default
9
11
  # branch, not a one-shot timer, so resuming at a recorded reset time means
@@ -46,6 +48,11 @@
46
48
  # The job's `actions: write` covers listing and downloading its own
47
49
  # workflow's artifacts.
48
50
  #
51
+ # THE PERMISSIONS. `issues: write` and `pull-requests: write` serve `poll`'s
52
+ # reports, which comment on the waiting run's issue or pull request; a
53
+ # workflow-level `permissions:` block sets every permission it does not list to
54
+ # `none`.
55
+ #
49
56
  # WHAT IT WRITES. The `harness-poll-state` artifact: the failed-dispatch count
50
57
  # `poll` carries to the next tick, kept 7 days.
51
58
  #
@@ -83,6 +90,8 @@ on:
83
90
  permissions:
84
91
  contents: read
85
92
  actions: write
93
+ issues: write
94
+ pull-requests: write
86
95
 
87
96
  defaults:
88
97
  run:
@@ -4,6 +4,8 @@
4
4
  # WHO WRITES IT. `autonomous-sdlc-harness init`, only when harness.config.json's
5
5
  # `execution.target` is `github-actions`. Written create-if-absent: from then on
6
6
  # it is yours to tune, and a re-run of `init` keeps your copy.
7
+ # `init --upgrade-workflows` re-renders it at that CLI's version, after a `.bak`,
8
+ # and only when the HARNESS_CLI_VERSION below differs.
7
9
  #
8
10
  # THE INPUT CONTRACT. Composed on the shell side by `remote-run.sh` alone:
9
11
  # action run | pause | warm | stop. Only `run` and `warm` start a
@@ -30,13 +32,42 @@
30
32
  # cli/src/remote/githubActions.ts the file names, HARNESS_RUNNER,
31
33
  # HARNESS_REMOTE_STOP, CLAUDE_CODE_OAUTH_TOKEN,
32
34
  # ANTHROPIC_API_KEY, HARNESS_PUSH_URL,
33
- # HARNESS_GIT_TOKEN, the artifact harness-state
35
+ # HARNESS_GIT_TOKEN, the artifact harness-state,
36
+ # HARNESS_CLI_VERSION
37
+ # cli/src/generators/githubWorkflows.ts --upgrade-workflows
34
38
  # cli/src/generators/projectSettings.ts the marketplace and plugin key
35
39
  # autonomous-sdlc-harness@autonomous-sdlc-harness
36
40
  # cli/src/config/model.ts DEFAULTS.scriptsDir and retrievalApplies
37
41
  # cli/src/machine/paths.ts machineCacheDir, under which the retrieval
38
42
  # runtime sits in `retrieval/`
39
43
  # cli/src/retrieval/runtime.ts RETRIEVAL_CACHE_DIRNAME, that `retrieval/`
44
+ # remote-run.sh (the scriptsDir copy) RUN_JOB_NAME (`run`): the `run` job has no
45
+ # `name:`, so GitHub names it by its key,
46
+ # and the settledness test reads that
47
+ # job's status by that name
48
+ # harness-control.yml the concurrency group
49
+ # `harness-review-<branch>`, shared by its
50
+ # review jobs and the `collect` job here
51
+ #
52
+ # THE COLLECT JOB. When a run ends, `remote-run.sh collect` starts the next
53
+ # user-review round from every review requesting changes that `control`
54
+ # answered "collected" while the run was in flight, so a reviewer waits at most
55
+ # for that run and never resubmits. It is a job of its own, not a step of
56
+ # `run`, for two reasons: a step cannot hold a concurrency group, and this one
57
+ # must share `harness-review-<branch>` with harness-control.yml's review jobs so
58
+ # the two never place a round at once; and the settledness test reads "the
59
+ # `run` job is completed", which no step inside that job could see. Under
60
+ # `!cancelled()` a failed `run` job still reaches it, and the run's state bundle
61
+ # decides whether the run ended or only paused or parked; a cancelled or
62
+ # stopped run skips it. The group name mirrors harness-control.yml's, and
63
+ # RUN_JOB_NAME in remote-run.sh mirrors the `run` job's key (DECLARED MIRRORS
64
+ # above). It reads no secret: its own token pushes the round, dispatches it and
65
+ # comments. It checks out the default branch, as harness-control.yml's review
66
+ # jobs do, never the run's branch: `collect` fetches that branch itself and
67
+ # `review` cuts its own working copy of it, which create-worktree.sh refuses
68
+ # for a branch already checked out. Its step is `continue-on-error` for the
69
+ # report step's reason below: scripts older than this file carry no `collect`
70
+ # verb.
40
71
  #
41
72
  # ACTION PINS.
42
73
  # actions/checkout@v5
@@ -58,7 +89,9 @@
58
89
  # Secrets: CLAUDE_CODE_OAUTH_TOKEN and/or ANTHROPIC_API_KEY (one is required;
59
90
  # billing follows the API key when both are set), HARNESS_GIT_TOKEN (optional:
60
91
  # a personal or App token, so the job's pushes trigger your own CI, which
61
- # GITHUB_TOKEN pushes never do), HARNESS_PUSH_URL (optional notifications).
92
+ # GITHUB_TOKEN pushes never do; it also opens the run's draft pull request,
93
+ # so your CI runs on it without an approval click), HARNESS_PUSH_URL
94
+ # (optional notifications).
62
95
  # Variables: HARNESS_RUNNER, HARNESS_REMOTE_STOP (any value stops every job
63
96
  # before it launches anything), HARNESS_STEP_TIMEOUT_MINUTES,
64
97
  # HARNESS_SELF_PAUSE_AFTER_MINUTES, HARNESS_MAX_CHAIN, and the watcher
@@ -67,6 +100,20 @@
67
100
  # harness.config.json, at run time: `scriptsDir` and whether docs retrieval
68
101
  # applies. No configured value is frozen into this file.
69
102
  #
103
+ # THE PERMISSIONS. A workflow-level `permissions:` block sets every permission
104
+ # it does not list to `none`, so each one the job uses is listed:
105
+ # contents to push the branch
106
+ # actions to dispatch, poll and enable the poller
107
+ # issues to comment and set labels on the issue and the pull request,
108
+ # which both use the issues API
109
+ # pull-requests to open the draft pull request and label it
110
+ #
111
+ # WHY THE REPORT STEP MAY FAIL. `Open the pull request and report` runs
112
+ # `remote-run.sh deliver` after the push, so the pull request's head carries the
113
+ # run's last commit. It is `continue-on-error`: a repository that re-rendered
114
+ # this file without `init --force` still runs scripts with no `deliver` verb,
115
+ # and a finished run must not be marked failed for its report.
116
+ #
70
117
  # THE SELF-PAUSE POINT, MEASURED. On a hosted runner the job drops its own PAUSE
71
118
  # REMOTE_SELF_PAUSE_AFTER_SECS after it started, the run yields at its next
72
119
  # dispatch boundary, and `remote-run.sh continue` chains a new job.
@@ -95,13 +142,18 @@
95
142
  # accepted and its `Run the harness` step run: the expression-valued
96
143
  # timeout-minutes is accepted.
97
144
  #
98
- # THE PLUGIN PIN. `claude plugin marketplace add --help` and
99
- # `claude plugin install --help` (Claude Code 2.1.282) offer no ref or version:
100
- # "Usage: claude plugin marketplace add [options] <source>" (options: --claudeai,
101
- # --scope, --sparse); "Usage: claude plugin install|i [options] <plugin>".
102
- # So the job installs the plugin, then refuses to run, naming both versions,
103
- # when `claude plugin list --json` reports a version other than this file's
104
- # rendered CLI version.
145
+ # THE PLUGIN PIN. The job clones the marketplace repository at the release tag
146
+ # autonomous-sdlc-harness--v<HARNESS_CLI_VERSION> (docs/development.md, section 7)
147
+ # and adds the clone as a directory source, so what it installs is the tag's
148
+ # plugin whatever the default branch carries. Both the add and the install run
149
+ # from $RUNNER_TEMP, outside the checkout, so the committed .claude/settings.json's
150
+ # project-scope entry of the same name is not in view while installing. The
151
+ # clone is the plugin's runtime root and the runner's cache is its install
152
+ # root; `init --plugin-root-entries` grants both and `doctor --remote-job` grades both. The check after install
153
+ # stays as the guard: the job refuses to run, naming both versions and the
154
+ # upgrade route, when `claude plugin list --json` reports another version. What
155
+ # the agent-runner CLI was measured to accept is recorded in
156
+ # docs/remote-execution.md, section 6.
105
157
  #
106
158
  # WHY retention-days IS SET. The `harness-state` bundle is the only remote copy
107
159
  # of a run's clarifications, uncommitted planning drafts and carried counts; a
@@ -174,6 +226,8 @@ on:
174
226
  permissions:
175
227
  contents: write
176
228
  actions: write
229
+ issues: write
230
+ pull-requests: write
177
231
 
178
232
  defaults:
179
233
  run:
@@ -337,11 +391,18 @@ jobs:
337
391
  echo "::error::.claude/settings.json names no marketplace source for autonomous-sdlc-harness. Run init locally with --marketplace <owner>/<repo> and commit the file."
338
392
  exit 1
339
393
  fi
340
- claude plugin marketplace add "$source_repo"
341
- claude plugin install autonomous-sdlc-harness@autonomous-sdlc-harness
394
+ upgrade_route="to run another version, re-render this repository's workflows with 'npx autonomous-sdlc-harness@<version> init --upgrade-workflows', then commit the paths its printed 'git add' names and push them to the default branch (docs/remote-execution.md, section 7, Upgrading)"
395
+ tag="autonomous-sdlc-harness--v$HARNESS_CLI_VERSION"
396
+ marketplace_dir="$RUNNER_TEMP/harness-marketplace"
397
+ # --quiet does not cover the detached-HEAD advice a tag checkout prints; only advice.detachedHead gates it.
398
+ if ! git -c advice.detachedHead=false clone --quiet --depth 1 --branch "$tag" "https://github.com/$source_repo.git" "$marketplace_dir"; then
399
+ echo "::error::$source_repo has no release tag $tag, so the plugin this workflow was rendered for cannot be installed. If $HARNESS_CLI_VERSION was released, its release is missing the tag; otherwise, $upgrade_route."
400
+ exit 1
401
+ fi
402
+ (cd "$RUNNER_TEMP" && claude plugin marketplace add ./harness-marketplace && claude plugin install autonomous-sdlc-harness@autonomous-sdlc-harness)
342
403
  installed=$(claude plugin list --json | jq -r '[.[] | select(.id == "autonomous-sdlc-harness@autonomous-sdlc-harness" and .scope == "user")][0].version // empty')
343
404
  if [ "$installed" != "$HARNESS_CLI_VERSION" ]; then
344
- echo "::error::the installed plugin is version '${installed:-none}', but this workflow was rendered for $HARNESS_CLI_VERSION. Publish the matching plugin, or re-run init with the installed version and commit the workflow."
405
+ echo "::error::the installed plugin is version '${installed:-none}', but this workflow was rendered for $HARNESS_CLI_VERSION and cloned $tag; $upgrade_route."
345
406
  exit 1
346
407
  fi
347
408
 
@@ -404,6 +465,13 @@ jobs:
404
465
  overwrite: true
405
466
  retention-days: 400
406
467
 
468
+ - name: Open the pull request and report
469
+ if: ${{ !cancelled() && env.SCRIPTS_DIR != '' }}
470
+ continue-on-error: true
471
+ env:
472
+ HARNESS_PR_TOKEN: ${{ secrets.HARNESS_GIT_TOKEN }}
473
+ run: bash "$SCRIPTS_DIR/remote-run.sh" deliver "$HARNESS_INPUT_BRANCH" "$RUNNER_TEMP/harness-state"
474
+
407
475
  - name: Continue, wait or stop
408
476
  if: ${{ !cancelled() && env.SCRIPTS_DIR != '' }}
409
477
  run: bash "$SCRIPTS_DIR/remote-run.sh" continue "$HARNESS_INPUT_BRANCH" "$RUNNER_TEMP/harness-state"
@@ -414,6 +482,53 @@ jobs:
414
482
  export HARNESS_REPO_SLUG="$HARNESS_REMOTE_SLUG"
415
483
  bash "$SCRIPTS_DIR/autonomous-notify.sh" failed "$HARNESS_INPUT_BRANCH" "" \
416
484
  "job cancelled: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" || true
485
+ bash "$SCRIPTS_DIR/remote-run.sh" report failed "$HARNESS_INPUT_BRANCH" --note "the job was cancelled: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" || true
486
+
487
+ collect:
488
+ needs: run
489
+ if: ${{ inputs.action == 'run' && !cancelled() }}
490
+ runs-on: ${{ vars.HARNESS_RUNNER || 'ubuntu-latest' }}
491
+ concurrency:
492
+ group: harness-review-${{ inputs.branch }}
493
+ cancel-in-progress: false
494
+ env:
495
+ GH_TOKEN: ${{ github.token }}
496
+ HARNESS_REMOTE_SLUG: ${{ github.repository }}
497
+ HARNESS_INPUT_BRANCH: ${{ inputs.branch }}
498
+ HARNESS_REMOTE_STOP: ${{ vars.HARNESS_REMOTE_STOP }}
499
+ HARNESS_TRIGGER_ALLOWED_BOTS: ${{ vars.HARNESS_TRIGGER_ALLOWED_BOTS }}
500
+ steps:
501
+ - name: Check out the default branch for collecting
502
+ uses: actions/checkout@v5
503
+ with:
504
+ ref: ${{ github.event.repository.default_branch }}
505
+ token: ${{ github.token }}
506
+ fetch-depth: 0
507
+
508
+ - name: Check for jq and gh before collecting
509
+ run: |
510
+ missing=""
511
+ for tool in jq gh; do
512
+ command -v "$tool" >/dev/null 2>&1 || missing="$missing $tool"
513
+ done
514
+ if [ -n "$missing" ]; then
515
+ echo "::error::this runner lacks:$missing. Install them on the self-hosted runner, or use a GitHub-hosted one."
516
+ exit 1
517
+ fi
518
+
519
+ - name: Read the configuration for collecting
520
+ run: |
521
+ scripts_dir=$(jq -r '.scriptsDir // "scripts"' harness.config.json)
522
+ echo "SCRIPTS_DIR=${scripts_dir%/}" >> "$GITHUB_ENV"
523
+
524
+ - name: Set the git identity for collecting
525
+ run: |
526
+ git config user.name "github-actions[bot]"
527
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
528
+
529
+ - name: Start the next round from collected reviews
530
+ continue-on-error: true
531
+ run: bash "$SCRIPTS_DIR/remote-run.sh" collect "$HARNESS_INPUT_BRANCH"
417
532
 
418
533
  warm:
419
534
  if: inputs.action == 'warm'
@@ -0,0 +1,144 @@
1
+ # harness-trigger.yml — starts an autonomous-sdlc-harness task run when an
2
+ # issue is labelled, or when a `repository_dispatch` asks for one.
3
+ #
4
+ # WHO WRITES IT. `autonomous-sdlc-harness init`, only when harness.config.json's
5
+ # `forge` is `github` and its `execution.target` is `github-actions`. Written
6
+ # create-if-absent: from then on it is yours to tune, and a re-run of `init`
7
+ # keeps your copy. `init --upgrade-workflows` does NOT re-render it: it carries
8
+ # no version pin and calls the scripts on the default branch, so it shares
9
+ # their re-run contract — `init --force` replaces it after a `.bak`.
10
+ #
11
+ # WHAT IT DOES. One job runs `remote-run.sh trigger`, which does everything
12
+ # else: it authorises the start, derives the branch, places and commits the task
13
+ # prompt, pushes, dispatches harness-run.yml, comments on the issue and removes
14
+ # the label. Every decision lives in that script; this file carries only the
15
+ # events, the permissions and the label filter. The `run-name` below is not
16
+ # harness-run.yml's `harness <action> <branch>` title, so no run lookup
17
+ # `remote-run.sh` makes by that title can match a trigger run.
18
+ #
19
+ # THE EVENTS.
20
+ # issues, type `labeled` only, never `opened`: labelling an issue at creation
21
+ # raises `labeled` too, so listening to both would start twice
22
+ # (docs/github-integration-research.md, T1). The job runs only for the
23
+ # label the HARNESS_TRIGGER_LABEL variable names (`sdlc-harness` when
24
+ # unset), and passes the job that same label, never an empty one;
25
+ # every other label yields a skipped job, which runs no runner and bills
26
+ # nothing (docs/remote-execution.md, `### Verified in Gate 12 round 2`).
27
+ # repository_dispatch, type `harness-task`: the entry for any other adapter
28
+ # (a Jira rule, a script). Its body is
29
+ # {"event_type": "harness-task",
30
+ # "client_payload": {"title": …, "body": …, "source": …}}
31
+ # within GitHub's client_payload limits (10 top-level properties, under
32
+ # 64 KB). Sending one needs a token with Contents write, so its holder is
33
+ # the authority.
34
+ #
35
+ # WHO CAN START A RUN. `remote-run.sh`'s `trigger` paragraph owns the check —
36
+ # the labeller, bots and HARNESS_TRIGGER_ALLOWED_BOTS, the permission call —
37
+ # and the order its refusals are made in. Nothing here pre-filters on it.
38
+ #
39
+ # THE PERMISSIONS, declared because the default token may be read-only
40
+ # (docs/github-integration-research.md, T2):
41
+ # contents: write push the new branch carrying the task prompt
42
+ # actions: write dispatch harness-run.yml and look up the run it created
43
+ # issues: write comment on the issue and remove the trigger label
44
+ # The job reads no repository secret: it needs only its own token, so the
45
+ # credential secrets never reach the job that reads untrusted issue text.
46
+ #
47
+ # THE CHECKOUT. The default branch, with the job's own token and never
48
+ # HARNESS_GIT_TOKEN: a branch pushed with GITHUB_TOKEN starts no workflow
49
+ # (docs/remote-execution.md, `### Verified in Gate 12 round 3`), so the prompt
50
+ # commit does not run your CI. A full fetch, because pushing a new branch from
51
+ # a shallow clone has not been measured anywhere.
52
+ #
53
+ # NO CONCURRENCY GROUP. A group keeps at most one pending run and cancels an
54
+ # earlier pending one, which would drop a trigger. Two starts that race for one
55
+ # branch name are caught by the refused push of an existing branch, and the
56
+ # refusal is commented on the issue.
57
+ #
58
+ # TWO RULES EVERY EDIT KEEPS.
59
+ # * Event text and variables reach a shell line only through `env:` and the
60
+ # event file, never through a GitHub expression inside `run:` — an issue
61
+ # title is written by whoever opened the issue (script injection).
62
+ # * Every GitHub expression has a space after its opening braces. `init`
63
+ # renders its workflows with the CLI's double-brace token renderer, which
64
+ # reads a letter straight after the braces as a token; this file carries
65
+ # no token.
66
+ #
67
+ # DECLARED MIRRORS — a rename on either side is an edit to both:
68
+ # cli/src/remote/githubActions.ts WORKFLOW_TRIGGER_FILE, HARNESS_RUNNER,
69
+ # HARNESS_REMOTE_STOP, HARNESS_TRIGGER_LABEL,
70
+ # DEFAULT_TRIGGER_LABEL (`sdlc-harness`),
71
+ # HARNESS_TRIGGER_ALLOWED_BOTS,
72
+ # TRIGGER_DISPATCH_EVENT_TYPE (`harness-task`)
73
+ # cli/src/config/model.ts DEFAULTS.scriptsDir
74
+ #
75
+ # ACTION PINS.
76
+ # actions/checkout@v5
77
+ # The lowest major whose own action.yml declares `runs.using: node24`, per a
78
+ # maintainer's lookups of its action.yml and release notes on
79
+ # 2026-09-29T08:21Z (UTC). That major's release notes require Actions Runner
80
+ # 2.327.1 or newer; a GitHub-hosted runner already has it, and a self-hosted
81
+ # one must run it (docs/remote-execution.md, section 8). A major tag, not a
82
+ # commit sha: it is GitHub's own `actions/` organisation, the owner of the
83
+ # runner and GITHUB_TOKEN this job already trusts, and a major tag takes the
84
+ # action's own patch and security releases where a sha would freeze your copy.
85
+ # Pin a sha in your copy if you want one.
86
+
87
+ name: harness-trigger
88
+ run-name: harness trigger ${{ github.event.issue.number || github.event.action }}
89
+
90
+ on:
91
+ issues:
92
+ types: [labeled]
93
+ repository_dispatch:
94
+ types: [harness-task]
95
+
96
+ permissions:
97
+ contents: write
98
+ actions: write
99
+ issues: write
100
+
101
+ defaults:
102
+ run:
103
+ shell: bash
104
+
105
+ jobs:
106
+ trigger:
107
+ if: github.event_name == 'repository_dispatch' || github.event.label.name == (vars.HARNESS_TRIGGER_LABEL || 'sdlc-harness')
108
+ runs-on: ${{ vars.HARNESS_RUNNER || 'ubuntu-latest' }}
109
+ env:
110
+ GH_TOKEN: ${{ github.token }}
111
+ HARNESS_REMOTE_STOP: ${{ vars.HARNESS_REMOTE_STOP }}
112
+ HARNESS_REMOTE_SLUG: ${{ github.repository }}
113
+ HARNESS_TRIGGER_LABEL: ${{ vars.HARNESS_TRIGGER_LABEL || 'sdlc-harness' }}
114
+ HARNESS_TRIGGER_ALLOWED_BOTS: ${{ vars.HARNESS_TRIGGER_ALLOWED_BOTS }}
115
+ steps:
116
+ - name: Check out the default branch
117
+ uses: actions/checkout@v5
118
+ with:
119
+ token: ${{ github.token }}
120
+ fetch-depth: 0
121
+
122
+ - name: Check for jq and gh
123
+ run: |
124
+ missing=""
125
+ for tool in jq gh; do
126
+ command -v "$tool" >/dev/null 2>&1 || missing="$missing $tool"
127
+ done
128
+ if [ -n "$missing" ]; then
129
+ echo "::error::this runner lacks:$missing. Install them on the self-hosted runner, or use a GitHub-hosted one."
130
+ exit 1
131
+ fi
132
+
133
+ - name: Read the configuration
134
+ run: |
135
+ scripts_dir=$(jq -r '.scriptsDir // "scripts"' harness.config.json)
136
+ echo "SCRIPTS_DIR=${scripts_dir%/}" >> "$GITHUB_ENV"
137
+
138
+ - name: Set the git identity
139
+ run: |
140
+ git config user.name "github-actions[bot]"
141
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
142
+
143
+ - name: Start the run
144
+ run: bash "$SCRIPTS_DIR/remote-run.sh" trigger
@@ -68,5 +68,10 @@
68
68
  # rule a wired repository carries an untracked file from its first `config set` onward. The leading
69
69
  # slash anchors it at the repository root, the only place the configuration is written.
70
70
  /{{configBackupFile}}
71
+ # The copies `init --upgrade-workflows` and `init --force` take of the two remote-execution workflows
72
+ # and of the permission profile. The upgrade's report names each workflow copy in a diff command, and
73
+ # the profile copy carries this machine's absolute paths just as the profile does. Every other .bak a
74
+ # forced run writes is left visible on purpose.
75
+ {{harnessBackups}}
71
76
  {{qaBrowserArtifacts}}
72
77
  {{docsRetrievalIndex}}