github-delivery-os 1.4.1 → 1.5.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.
@@ -21,7 +21,7 @@ Creating issues and comments in a repo is a visible, outward action — other co
21
21
  Before creating anything that depends on configuration, check the target repo actually has Delivery OS installed and configured — a silent no-op (nothing happens because a variable is unset) is more confusing than an upfront "this won't do much yet":
22
22
 
23
23
  - **Installed?** `gh api repos/<owner>/<repo>/contents/.github/workflows/authorize-deployment.yml --silent` (404 = not installed — suggest `npx github-delivery-os status .` or `install --with-templates` in that repo).
24
- - **Labels set up?** `gh label list --repo <owner>/<repo>` — look for `production`, `qa`, `qa-request`, `sprint`, `sprint-active`, `planning`, `declined`, `ready-for-deploy`. Missing labels mean `Setup Labels` hasn't been run there yet — offer to fix it directly rather than just reporting the gap: `gh workflow run setup-labels.yml --repo <owner>/<repo>` (it's a `workflow_dispatch` trigger, so this actually creates them on the spot). Confirm with the user first since it's a real change to their repo.
24
+ - **Labels set up?** `gh label list --repo <owner>/<repo>` — look for `production`, `qa`, `qa-request`, `sprint`, `sprint-child`, `planning`, `declined`, `ready-for-deploy`. Missing labels mean `Setup Labels` hasn't been run there yet — offer to fix it directly rather than just reporting the gap: `gh workflow run setup-labels.yml --repo <owner>/<repo>` (it's a `workflow_dispatch` trigger, so this actually creates them on the spot). Confirm with the user first since it's a real change to their repo. (`sprint-active` instead of `sprint-child` means the repo is on a pre-1.5.0 install — still valid, just the older label name.)
25
25
  - **Repo variables set?** `gh variable list --repo <owner>/<repo>` — look for `RELEASE_APPROVER`, `QA_APPROVER`, `QA_ASSIGNEES`. If unset, say so plainly: the issue will still get created, but `notify-release-approver` will ping the literal placeholder `release-approver`/`qa-approver`, not a real person. Setting these requires repo admin access (`gh variable set NAME --repo <owner>/<repo> --body <value>`) — don't set them without being asked to, since they name a real person as approver.
26
26
 
27
27
  ## Creating issues
@@ -32,7 +32,7 @@ Before creating anything that depends on configuration, check the target repo ac
32
32
 
33
33
  This confirm-first default is for issues created **on explicit request** ("file a bug for this", "create a sprint"). The "Autonomous tracking" section below describes a *different* mode — noticing and filing work on its own during a session — and overrides this default there: act first, confirm after, per its own "Confirm only when unsure" rule. Don't apply both rules to the same action.
34
34
 
35
- **Sprint Planning** — triggers `sprint-child-creator` (one child issue per feature line, each labeled `sprint-active`, on open):
35
+ **Sprint Planning** — triggers `sprint-child-creator` (one child issue per feature line, each labeled `sprint-child` — `sprint-active` on repos installed before 1.5.0 — on open):
36
36
  - Title **must contain** the literal string `SPRINT -`, e.g. `SPRINT - Sprint 14`
37
37
  - Labels: `sprint`, `planning`
38
38
  - Body:
@@ -163,7 +163,7 @@ Closing a sprint task (child) issue is what actually moves the burn-down — cre
163
163
  gh issue close <number> --repo <owner>/<repo>
164
164
  ```
165
165
 
166
- `auto-close-sprint` fires on close, re-reads every `sprint-active` issue whose body contains `Parent Sprint: #<N>`, recomputes progress, and rewrites the sprint issue's `## 🚦 Sprint Status` section. At 100% it also closes the sprint issue itself and posts a completion comment. Re-check the sprint issue's body afterward to see the update — it happens as a side effect of closing the child, not as a response visible on the child issue itself.
166
+ `auto-close-sprint` fires on close, re-reads every issue whose body contains `Parent Sprint: #<N>` (label-independent — see below), recomputes progress, and rewrites the sprint issue's `## 🚦 Sprint Status` section. At 100% it also closes the sprint issue itself and posts a completion comment. Re-check the sprint issue's body afterward to see the update — it happens as a side effect of closing the child, not as a response visible on the child issue itself.
167
167
 
168
168
  ## Autonomous tracking (identify → file → update → close)
169
169
 
@@ -201,7 +201,7 @@ Don't rely on recalling an issue number from earlier in the conversation, and ne
201
201
  gh issue list --repo <owner>/<repo> --state open --search "<keywords from the work>"
202
202
  ```
203
203
 
204
- Narrow with `--label task` (or `bug`, `sprint-active`, `qa-request`) only once the category is known and the target repo actually has that label — don't assume `--label task` alone finds everything relevant.
204
+ Narrow with `--label task` (or `bug`, `sprint-child`, `qa-request`) only once the category is known and the target repo actually has that label — don't assume `--label task` alone finds everything relevant. (Older repos may still use `sprint-active` instead of `sprint-child`; check `gh label list` first.)
205
205
 
206
206
  This is also what makes picking work back up in a *new* session possible without any local memory — the issue list itself is the state.
207
207
 
@@ -232,7 +232,7 @@ Given an SRS/PRD, or just a plain-language feature description, break it into a
232
232
 
233
233
  3. **Per phase, once confirmed:**
234
234
  - Create the Sprint Planning issue with the usual recipe (`SPRINT - <phase name>`, dates, goal). "Sprint Features (One Per Line)" is template-required, so it can't be left empty — put one line noting the real breakdown is in linked Task issues (e.g. `See linked Task issues for this phase's breakdown`). This means exactly one bare placeholder child gets auto-created.
235
- - File one full Task issue per requirement (the usual Task recipe — Owner, Priority, Acceptance Criteria drawn from the spec text; ask only when the spec genuinely doesn't specify something, like priority). Include `Parent Sprint: #<sprint-number>` in the body (e.g. under Artifacts / Links) — that exact phrase is what `auto-close-sprint` actually scans for (`.github/workflows/auto-close-sprint.yml` filters on body content only, **not** the `sprint-active` label, deliberately — see its own comment on why label-scoping was tried and reverted). Add the `sprint-active` label anyway, for consistency with how the sprint's own children are found (see "Finding things" below), but know it plays no role in the burn-down count.
235
+ - File one full Task issue per requirement (the usual Task recipe — Owner, Priority, Acceptance Criteria drawn from the spec text; ask only when the spec genuinely doesn't specify something, like priority). Include `Parent Sprint: #<sprint-number>` in the body (e.g. under Artifacts / Links) — that exact phrase is what `auto-close-sprint` actually scans for (`.github/workflows/auto-close-sprint.yml` filters on body content only, **not** the `sprint-child` label, deliberately — see its own comment on why label-scoping was tried and reverted). Add the `sprint-child` label anyway, for consistency with how the sprint's own children are found (see "Finding things" below), but know it plays no role in the burn-down count.
236
236
  - Now that the real Task issues exist, close the one placeholder child: `gh issue close <N> --repo <owner>/<repo> --comment "Superseded by full Task issues for this phase — see #.., #.., #.."`, filling in the actual issue numbers just created. Otherwise the placeholder sits in the sprint's burn-down denominator as an item that can never represent real completed work, and the sprint can never legitimately reach 100%.
237
237
 
238
238
  4. **Report back everything created**, grouped by phase — sprint issue number, task issue numbers, and the placeholder-close.
@@ -245,4 +245,4 @@ When there's no issue number in hand yet:
245
245
  - **Production releases awaiting a decision:** `gh issue list --repo <owner>/<repo> --label production --state open`
246
246
  - **Active sprints:** `gh issue list --repo <owner>/<repo> --label sprint --state open` (title contains `SPRINT -`)
247
247
  - **Open QA requests:** `gh issue list --repo <owner>/<repo> --label qa-request --state open`
248
- - **A sprint's own children:** `gh issue list --repo <owner>/<repo> --label sprint-active --search "\"Parent Sprint: #<N>\" in:body"` — the exact-phrase quotes matter, otherwise the search matches "Parent", "Sprint", and the number as separate free-text terms instead of the literal phrase
248
+ - **A sprint's own children:** `gh issue list --repo <owner>/<repo> --label sprint-child --search "\"Parent Sprint: #<N>\" in:body"` — the exact-phrase quotes matter, otherwise the search matches "Parent", "Sprint", and the number as separate free-text terms instead of the literal phrase. On a repo installed before 1.5.0 (or one that hasn't run the `gh label edit` migration — see `docs/consumer-setup.md`), use `--label sprint-active` instead, or drop `--label` entirely and rely on the body search alone if you're not sure which name applies.
@@ -26,7 +26,7 @@ jobs:
26
26
  { name: 'intake', color: '0E8A16' },
27
27
  { name: 'bug', color: 'D93F0B' },
28
28
  { name: 'sprint', color: '1D76DB' },
29
- { name: 'sprint-active', color: '1D76DB' },
29
+ { name: 'sprint-child', color: '1D76DB', description: "Applied to a sprint's task-breakdown children on open; doesn't change when the sprint closes" },
30
30
  { name: 'planning', color: '5319E7' },
31
31
  { name: 'sprint-planning', color: '5319E7' },
32
32
  { name: 'task', color: '7057FF' },
@@ -40,13 +40,14 @@ jobs:
40
40
  { name: 'risk', color: 'B60205' },
41
41
  ];
42
42
  let created = 0;
43
- for (const { name, color } of labels) {
43
+ for (const { name, color, description } of labels) {
44
44
  try {
45
45
  await github.rest.issues.createLabel({
46
46
  owner: context.repo.owner,
47
47
  repo: context.repo.repo,
48
48
  name,
49
49
  color,
50
+ ...(description ? { description } : {}),
50
51
  });
51
52
  console.log(`Created: ${name}`);
52
53
  created++;
@@ -45,6 +45,6 @@ jobs:
45
45
  repo: context.repo.repo,
46
46
  title,
47
47
  body: bodyContent,
48
- labels: ['sprint-active']
48
+ labels: ['sprint-child']
49
49
  });
50
50
  }
@@ -77,7 +77,10 @@ jobs:
77
77
  fi
78
78
  fi
79
79
 
80
- if [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint-active') }}" == "true" ]]; then
80
+ # sprint-active is the pre-1.5.0 label name; still checked here so
81
+ # issues created before a repo upgrades don't go silent on close.
82
+ if [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint-child') }}" == "true" ]] || \
83
+ [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint-active') }}" == "true" ]]; then
81
84
  if [ "$ACTION" = "opened" ]; then
82
85
  MESSAGE="🟡🛠️ SPRINT TASK CREATED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
83
86
  elif [ "$ACTION" = "closed" ]; then
@@ -129,7 +132,8 @@ jobs:
129
132
  elif [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'production') }}" == "true" ]]; then
130
133
  MESSAGE="💬🚀 RELEASE COMMENT%0A$TITLE%0A$URL%0A---%0A$COMMENT%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
131
134
 
132
- elif [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint-active') }}" == "true" ]]; then
135
+ elif [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint-child') }}" == "true" ]] || \
136
+ [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint-active') }}" == "true" ]]; then
133
137
  MESSAGE="💬🛠️ SPRINT TASK COMMENT%0A$TITLE%0A$URL%0A---%0A$COMMENT%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
134
138
  fi
135
139
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-delivery-os",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "A GitHub-native Delivery Governance Framework for structured sprint execution, QA review, and collaborative production release control.",
5
5
  "main": "src/install.js",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { program } = require('commander');
3
+ const { program, Option } = require('commander');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
6
  const { runInstall, runStatus, runUninstall } = require('./install');
@@ -21,8 +21,12 @@ program
21
21
  .option('-t, --with-templates', 'Copy issue templates (sprint, task, bug, QA, production release)')
22
22
  .option('-l, --with-labels', 'Create labels via gh CLI (requires gh auth)')
23
23
  .option('-s, --with-skill', 'Add the delivery-ops Claude Code skill (.claude/skills/delivery-ops/SKILL.md)')
24
- .option('-o, --overwrite', 'Replace existing workflow/template files')
25
- .option('--no-overwrite', 'Skip existing files (default)')
24
+ .option('-u, --update', 'Replace existing workflow/template files with the latest version')
25
+ .option('--no-update', 'Skip existing files (default)')
26
+ // Pre-1.5.0 names, kept working silently so existing scripts/CI calling
27
+ // `install --overwrite` don't break — --update is the documented name now.
28
+ .addOption(new Option('-o, --overwrite').hideHelp())
29
+ .addOption(new Option('--no-overwrite').hideHelp())
26
30
  .option('-d, --dry-run', 'Show what would happen without changing files')
27
31
  .action((target, options) => {
28
32
  const targetDir = target || '.';
@@ -31,7 +35,7 @@ program
31
35
  withTemplates: options.withTemplates ?? false,
32
36
  withLabels: options.withLabels ?? false,
33
37
  withSkill: options.withSkill ?? false,
34
- overwrite: options.overwrite ?? false,
38
+ overwrite: options.update ?? options.overwrite ?? false,
35
39
  dryRun: options.dryRun ?? false,
36
40
  });
37
41
  });
package/src/install.js CHANGED
@@ -51,7 +51,7 @@ const LABELS = [
51
51
  ['intake', '0E8A16'],
52
52
  ['bug', 'D93F0B'],
53
53
  ['sprint', '1D76DB'],
54
- ['sprint-active', '1D76DB'],
54
+ ['sprint-child', '1D76DB', "Applied to a sprint's task-breakdown children on open; doesn't change when the sprint closes"],
55
55
  ['planning', '5319E7'],
56
56
  ['sprint-planning', '5319E7'],
57
57
  ['task', '7057FF'],
@@ -351,9 +351,11 @@ function runInstall(options) {
351
351
  }
352
352
 
353
353
  if (!labelsSkipReason) {
354
- for (const [name, color] of LABELS) {
354
+ for (const [name, color, description] of LABELS) {
355
355
  try {
356
- execFileSync('gh', ['label', 'create', name, '--color', color], {
356
+ const args = ['label', 'create', name, '--color', color];
357
+ if (description) args.push('--description', description);
358
+ execFileSync('gh', args, {
357
359
  cwd: targetAbs,
358
360
  stdio: 'pipe',
359
361
  });
@@ -406,11 +408,11 @@ function runInstall(options) {
406
408
  if (templatesPresentButNotTouched || skillPresentButNotTouched) {
407
409
  console.log(' Note: previously-installed templates and/or the Claude Code skill exist');
408
410
  console.log(' on disk but were not requested this run, so the recorded Delivery OS');
409
- console.log(' version was not updated. Re-run with --overwrite plus --with-templates');
411
+ console.log(' version was not updated. Re-run with --update plus --with-templates');
410
412
  console.log(' and/or --with-skill to bring everything (and the recorded version) in sync.');
411
413
  } else {
412
414
  console.log(' Note: some files already existed and were skipped, so the recorded');
413
- console.log(' Delivery OS version was not updated. Re-run with --overwrite to sync');
415
+ console.log(' Delivery OS version was not updated. Re-run with --update to sync');
414
416
  console.log(' all files (and the recorded version) to the latest release.');
415
417
  }
416
418
  console.log('');
@@ -453,7 +455,7 @@ function runInstall(options) {
453
455
  console.log('Dry run complete. No files were changed.');
454
456
  } else {
455
457
  console.log('No new files created (existing files were skipped).');
456
- console.log('To update: use --overwrite (run with --dry-run first to preview).');
458
+ console.log('To update: use --update (run with --dry-run first to preview).');
457
459
  }
458
460
  }
459
461
  console.log('');
@@ -464,11 +466,11 @@ function runInstall(options) {
464
466
  // installed actually gets touched this run (see the cleanInstall check
465
467
  // there) — so a repo with templates and/or the skill already on disk needs
466
468
  // --with-templates/--with-skill passed again on an update, not just
467
- // --overwrite, or the recorded version never advances and `status` keeps
469
+ // --update, or the recorded version never advances and `status` keeps
468
470
  // suggesting the same command forever. Build the hint from what's actually
469
471
  // on disk so it's never wrong.
470
- function buildOverwriteCommand({ hasTemplates, hasSkill }) {
471
- const flags = [hasTemplates ? '--with-templates' : null, hasSkill ? '--with-skill' : null, '--overwrite']
472
+ function buildUpdateCommand({ hasTemplates, hasSkill }) {
473
+ const flags = [hasTemplates ? '--with-templates' : null, hasSkill ? '--with-skill' : null, '--update']
472
474
  .filter(Boolean)
473
475
  .join(' ');
474
476
  return `npx github-delivery-os@latest install ${flags} .`;
@@ -533,7 +535,7 @@ async function runStatus(options) {
533
535
  } else {
534
536
  console.log('Installed version: unknown (installed before version tracking was added)');
535
537
  console.log(
536
- ` Run: ${buildOverwriteCommand({ hasTemplates: installedTemplates.length > 0, hasSkill: skillInstalled })}`
538
+ ` Run: ${buildUpdateCommand({ hasTemplates: installedTemplates.length > 0, hasSkill: skillInstalled })}`
537
539
  );
538
540
  }
539
541
 
@@ -546,7 +548,7 @@ async function runStatus(options) {
546
548
  } else if (manifest && manifest.version) {
547
549
  console.log(`⬆️ Update available: ${manifest.version} → ${latest}`);
548
550
  console.log(
549
- ` Run: ${buildOverwriteCommand({ hasTemplates: installedTemplates.length > 0, hasSkill: skillInstalled })}`
551
+ ` Run: ${buildUpdateCommand({ hasTemplates: installedTemplates.length > 0, hasSkill: skillInstalled })}`
550
552
  );
551
553
  } else {
552
554
  console.log(`Latest published version: ${latest}`);
@@ -568,7 +570,7 @@ async function runStatus(options) {
568
570
  });
569
571
  console.log(' That workflow will fail with MODULE_NOT_FOUND the next time it runs.');
570
572
  console.log(
571
- ` Fix: ${buildOverwriteCommand({ hasTemplates: installedTemplates.length > 0, hasSkill: skillInstalled })}`
573
+ ` Fix: ${buildUpdateCommand({ hasTemplates: installedTemplates.length > 0, hasSkill: skillInstalled })}`
572
574
  );
573
575
  console.log('');
574
576
  }
@@ -579,7 +581,7 @@ async function runStatus(options) {
579
581
  console.log(' require()s a script under .github/scripts will fail with "module is not');
580
582
  console.log(' defined in ES module scope" the next time it runs.');
581
583
  console.log(
582
- ` Fix: ${buildOverwriteCommand({ hasTemplates: installedTemplates.length > 0, hasSkill: skillInstalled })}`
584
+ ` Fix: ${buildUpdateCommand({ hasTemplates: installedTemplates.length > 0, hasSkill: skillInstalled })}`
583
585
  );
584
586
  console.log('');
585
587
  }
@@ -768,7 +770,7 @@ module.exports = {
768
770
  readManifest,
769
771
  writeManifest,
770
772
  fetchLatestVersion,
771
- buildOverwriteCommand,
773
+ buildUpdateCommand,
772
774
  skillPath,
773
775
  SKILL_REL_PATH,
774
776
  WORKFLOWS,
@@ -776,5 +778,6 @@ module.exports = {
776
778
  SCRIPTS,
777
779
  SCRIPTS_PACKAGE_JSON,
778
780
  REQUIRED_SCRIPT_BY_WORKFLOW,
781
+ LABELS,
779
782
  },
780
783
  };