@troylar/flightdeck 2.7.0 → 2.9.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,39 @@
2
2
 
3
3
  <!-- Generated by build.py from src/releases.json. Edit that file, not this one. -->
4
4
 
5
+ ## 2.9.0 · Oct 27, 2026
6
+
7
+ Team values.
8
+
9
+ ### New
10
+
11
+ - **Keep the team's shared settings in one place** (Settings). Settings › Team values holds settings the whole team uses, like a Bitbucket base URL, each with what it's for. The folder owner and backup owner add, change and remove them; everyone else sees them. Values are trimmed, and the page reminds you not to store passwords or tokens there. If someone else changed the list first, nothing is saved over it: the list reloads and you make the change again. *Where:* Settings, Team values
12
+ - **Pass a team value to a linked run** (Editor). A child detail's source can be A team value, chosen from the folder's list with what each one is for and what it is now. The Markdown gets pass: base=team:bitbucket-base-url. A name that isn't in the folder is flagged in the editor and stops publishing. *Where:* Editing a draft, a Run another process step
13
+ - **The child gets the team value as it is when it starts** (Runs). The team value is read when the child starts and copied into it, so a later change never changes a started child. One that isn't set is asked for this run. If it changes while you fill in the form or while the child is saved, nothing starts and the form opens again with the new value and your answers. A child that changes it doesn't complete the step. *Where:* The run screen, a Run another process step
14
+
15
+ ### Improved
16
+
17
+ - **Link it asks you to confirm team values** (Runs). A run started for a step but never linked can still be linked. On a step that passes team values, Link it shows the run's values and links them only once you confirm them, as shown. If the run changes while you're looking, it shows the new values first. *Where:* The run screen, a run that isn't linked
18
+ - **Folder health checks team-values.json** (Folder health). A team-values.json that can't be read, a conflict copy of it, and a published step whose team value was removed later are each listed with what to do. *Where:* Settings, Folder health
19
+ - **A checklist that passes a team value needs Flightdeck 2.9** (Compatibility). A version that passes a team value, and its runs, open only in Flightdeck 2.9 or later; earlier copies name the version and won't run it. A checklist without one publishes exactly as before. *Where:* Publishing and older copies
20
+
21
+ ## 2.8.0 · Oct 26, 2026
22
+
23
+ Nested processes that know what the child needs.
24
+
25
+ ### New
26
+
27
+ - **Pick the process a step runs from a list** (Editor). A Run another process step chooses its process from a list of this folder's checklists, with each one's latest version, and picks Latest or one version to always run. Other ID… still takes a process that isn't here yet. *Where:* Editing a draft, a Run another process step
28
+ - **Say where each of the child's details comes from** (Editor). What the child needs lists every run detail of the checklist it runs. Each one comes from one of this run's details (under another name if you like), a fixed value, or is asked when the child starts. The Markdown gets a pass: line, for example pass: environment=^env, region=us-east-1, notify=?. *Where:* Editing a draft, a Run another process step
29
+ - **Publishing checks what a step passes against the child as it is now** (Checks). A name the child doesn't have, a fixed value of the wrong kind, or a detail that isn't listed stops publishing, read from the folder at that moment. A child published later without a detail a step names is flagged in Folder health, with a way to open the checklist that needs changing. *Where:* Checks, publishing and Folder health
30
+ - **The child's start form opens already filled in** (Runs). Starting a child shows what this run passes and where each value comes from, and asks only what's left, with the roles. A value that doesn't fit keeps the form open with what you typed. If a passed detail changes while you fill it in, nothing starts and it says why. With nothing to ask, the child starts at once, as before. *Where:* The run screen, a Run another process step
31
+ - **Each step says which details it uses and sets** (Editor). A selected step lists the run details it uses (its text, fields, conditions and what it passes) and the ones it sets. *Where:* Editing a draft, the step form
32
+
33
+ ### Improved
34
+
35
+ - **Rename keeps what linked steps pass** (Editor). Renaming a detail rewrites this checklist's carry: and pass: lines so each child detail gets the same value. A checklist that runs this one at its latest and passes the old name gets a warning with the fix, and Folder health flags it once this is published. *Where:* Editing a draft, Checklist details
36
+ - **A checklist with a pass: line needs Flightdeck 2.8** (Compatibility). A version with a pass: line, and its runs, open only in Flightdeck 2.8 or later; earlier copies name the version and won't run it. A checklist without one publishes exactly as before and runs in every 2.x copy. *Where:* Publishing and older copies
37
+
5
38
  ## 2.7.0 · Oct 26, 2026
6
39
 
7
40
  Boxes that know your details.
package/README.md CHANGED
@@ -111,10 +111,14 @@ You don't have to write Markdown. **New process** on the Processes page asks for
111
111
 
112
112
  **The step form** asks a question for each box, with a gray example and why it matters. It shows only the boxes the step's type usually needs, and any that hold a value; the rest are one click away behind **+ add** buttons. The fields every step accepts (a link, how long it takes, a deadline, proof, a time) are under **More about this step**. A choice of five or fewer is a row of buttons. **When it's checked, they record** lists exactly what a run will ask for. In the outline, drag a step or press Alt+Up / Alt+Down to move it, and **Undo** and **Redo** step through changes.
113
113
 
114
- **Details in any box.** Type `^` in a step's text, a gate's approval or a text field to pick a run detail, or use **Insert a detail**. A misspelled one is marked with a one-click fix ("Use ^environment"), and a detail that may not be set yet at that step says which step sets it. **In a run:** under the box shows it filled in, with the values from your latest run. A caret that isn't a detail, like `grep ^ERROR`, stays as written. To rename a detail everywhere at once, use **Rename** in Checklist details; it refuses if another checklist receives the detail by that name.
114
+ **Details in any box.** Type `^` in a step's text, a gate's approval or a text field to pick a run detail, or use **Insert a detail**. A misspelled one is marked with a one-click fix ("Use ^environment"), and a detail that may not be set yet at that step says which step sets it. **In a run:** under the box shows it filled in, with the values from your latest run. A caret that isn't a detail, like `grep ^ERROR`, stays as written. To rename a detail everywhere at once, use **Rename** in Checklist details; a linked step in this checklist keeps passing the same value to its child. If another checklist runs this one at its latest and passes the detail by the old name, Rename says so, and Folder health flags that checklist once this version is published, until it's changed to match.
115
115
 
116
116
  <p align="center"><a href="https://github.com/troylar/flightdeck/blob/main/docs/images/autocomplete-light.png" title="Open in GitHub's image viewer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/troylar/flightdeck/main/docs/images/autocomplete-dark.png"><img src="https://raw.githubusercontent.com/troylar/flightdeck/main/docs/images/autocomplete-light.png" alt="A Jenkins step's parameters box: typing ^en offers ^environment, a text detail asked when a run starts, or adding ^en as a new run detail" width="49%"></picture></a> <a href="https://github.com/troylar/flightdeck/blob/main/docs/images/rename-light.png" title="Open in GitHub's image viewer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/troylar/flightdeck/main/docs/images/rename-dark.png"><img src="https://raw.githubusercontent.com/troylar/flightdeck/main/docs/images/rename-light.png" alt="Checklist details: each run detail with how many places use it and Rename; renaming ^tag to build_tag lists the steps that follow it" width="49%"></picture></a></p>
117
117
 
118
+ **Run another process.** A step can start another checklist as a linked run. Pick the process from a list, and say where each of its details comes from: one of this run's details (under another name if you like), a fixed value, or asked when it starts. Publishing checks that against the other checklist as it is now. When you start it, its form opens with what's passed already filled in and asks only what's left (2.8).
119
+
120
+ <p align="center"><a href="https://github.com/troylar/flightdeck/blob/main/docs/images/nested-light.png" title="Open in GitHub's image viewer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/troylar/flightdeck/main/docs/images/nested-dark.png"><img src="https://raw.githubusercontent.com/troylar/flightdeck/main/docs/images/nested-light.png" alt="A linked step in the editor: Check in code chosen from a list, Latest when this run starts, and what Check in code needs: branch asked when it starts, commit from this run's ^commit, story from this run's ^ticket" width="38%"></picture></a> <a href="https://github.com/troylar/flightdeck/blob/main/docs/images/child-start-light.png" title="Open in GitHub's image viewer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/troylar/flightdeck/main/docs/images/child-start-dark.png"><img src="https://raw.githubusercontent.com/troylar/flightdeck/main/docs/images/child-start-light.png" alt="Starting the Check in code run: commit and story passed from this run, branch asked, and who's doing what" width="60%"></picture></a></p>
121
+
118
122
  **Save draft** saves your work. **Review & publish** goes back to the process page: there you can compare the draft with the last version, write what changed in one line, and choose **Publish vN**. Anything that blocks publishing disables the button; suggestions don't. A save goes through only if the draft is still what you opened; if a teammate changed it meanwhile, nothing is saved and you choose between their version and yours. A shared folder can't lock a file, so edit a draft with one person (or one AI assistant) at a time.
119
123
 
120
124
  <a href="https://github.com/troylar/flightdeck/blob/main/docs/images/edit-light.png" title="Open in GitHub's image viewer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/troylar/flightdeck/main/docs/images/edit-dark.png"><img src="https://raw.githubusercontent.com/troylar/flightdeck/main/docs/images/edit-light.png" alt="Editing a draft as Markdown, with Practice, Save draft and Review &amp; publish in the header: the open corrections with links to their lines, the Markdown on the left, and the Checks and Preview tabs on the right"></picture></a>
@@ -219,7 +223,7 @@ roles: release-lead, qa, backend
219
223
  | `## Gate:` · `approves:` | The pause point, and exactly what passing it approves |
220
224
  | `### Track: Name @role` | Parallel work inside a phase |
221
225
  | `at:` · `due:` · `after:` | A clock time, a deadline, or a due time after the run starts |
222
- | `{run.process}` · `carry:` · `version:` | Start another process as a linked child run, passing these details, on an exact version if you name one |
226
+ | `{run.process}` · `carry:` · `pass:` · `version:` | Start another process as a linked child run, passing these details (under another name, as a fixed value, or asked when it starts, with `pass:`, 2.8; or a team value, `team:`, 2.9), on an exact version if you name one |
223
227
  | `group:` · `tags:` | Where the process sits in your hierarchy, and how to find it |
224
228
  | `context: rows:number, due:date` | Typed run details (number, yes-no, date, choice, link), checked as they're typed |
225
229
  | `> note` · `![caption](images/…)` | A note or screenshot, under a step, a phase or a gate |
@@ -304,6 +308,7 @@ More in [Flightdeck and OneDrive](https://github.com/troylar/flightdeck/blob/mai
304
308
 
305
309
  ```
306
310
  people.json who's on the team, with the folder owner and backup owner
311
+ team-values.json shared settings like a Bitbucket base URL, changed in Settings by the folder owner and backup owner
307
312
  maps/drafts/ checklists being written (Markdown)
308
313
  publications/ published versions, frozen (Flightdeck writes these)
309
314
  staged/ staged versions only test runs use, and each retire or reopen, written once
package/bin/flightdeck.js CHANGED
@@ -1500,7 +1500,32 @@ async function draftMain(argv) {
1500
1500
  try { eng = loadEngine(); dir = folderArg(args[2], 'draft ' + sub); } catch (e) { return draftFail(eng, 'failed', e.message); }
1501
1501
  const PF = eng.PF, PP = PF.proposals, U = PF.util, M = PF.map, now = Date.now();
1502
1502
  const session = val['--session'] || 's-' + U.randomId(12);
1503
- const checksOf = (t) => { const c = M.check(M.parse(t), { people: [], types: {}, workflows: {}, processes: [] }); return { blockers: c.blockers, suggestions: c.suggestions }; };
1503
+ /* The same checks the app shows, with each linked step's child read from publications/ (2.8), so what a step passes is
1504
+ checked against the child as it is in the folder. */
1505
+ const checksOf = async (t) => {
1506
+ const map = M.parse(t), procs = {}, names = [];
1507
+ M.steps(map).forEach((x) => { const f = x.step.fields || {}; if (x.step.type === 'run.process' && f.process && names.indexOf(f.process) < 0) names.push(f.process); });
1508
+ /* The children are read through a problem ledger of their own, so a child that couldn't be read makes the answer partial. */
1509
+ const L = PF.repo.ledger(readAdapter(dir, { maxBytes: READ_MAX })), rfs = L.fs;
1510
+ for (const n of names) {
1511
+ try {
1512
+ const p = await PF.repo.readProcess(rfs, n);
1513
+ if (p) procs[n] = p;
1514
+ /* A version that read but can't be used is named as READ names one; a failed read the ledger already has. */
1515
+ (p ? p.broken : []).forEach((x) => { if (!x._readFailed) { const k = PF.appjson.pubProblem(x); L.note('publications/' + n + '/' + x.folder + '/', k.kind, k.why); } });
1516
+ } catch (e) { procs[n] = { latest: null, all: [], clashes: [], broken: [], unlisted: 'publications/' + n + '/ couldn\'t be read' }; }
1517
+ }
1518
+ /* Team values (2.9), through the same ledger: a failed read is noted by the ledger; damaged or newer content here. */
1519
+ let tv;
1520
+ if (M.steps(map).some((x) => x.step.type === 'run.process' && PF.links.teams(x.step).length)) {
1521
+ tv = await PF.repo.readTeamValues(rfs);
1522
+ if (tv.state === 'malformed' || tv.state === 'newer') L.note(PF.teamValues.FILE, tv.state, tv.why);
1523
+ }
1524
+ const c = M.check(map, { people: [], types: {}, workflows: {}, processes: [], children: PF.links.childrenFrom(procs), teamValues: tv });
1525
+ return { checks: { blockers: c.blockers, suggestions: c.suggestions }, problems: L.problems() };
1526
+ };
1527
+ /* Every problem once: the draft's reads, then the children's. */
1528
+ const merged = (a, b) => { const seen = {}, out = []; a.concat(b).forEach((p) => { const k = p.path + '\u0000' + p.kind; if (!seen[k]) { seen[k] = true; out.push(p); } }); return out; };
1504
1529
  try {
1505
1530
  if (sub === 'read') {
1506
1531
  const s = await draftState(eng, dir, proc, session);
@@ -1510,8 +1535,9 @@ async function draftMain(argv) {
1510
1535
  const cf = pending ? { rev: pending.rev, kind: 'proposal', ref: pending.id, text: pending.text }
1511
1536
  : s.draft.exists && s.draft.text != null ? { rev: s.draft.rev, kind: 'draft', ref: s.draft.path, text: s.draft.text }
1512
1537
  : latest ? { rev: latest.rev, kind: 'version', ref: latest.folder, text: latest.text } : { rev: PP.sha(''), kind: 'none', ref: null, text: '' };
1538
+ const ck = await checksOf(cf.text), problems = merged(s.problems, ck.problems);
1513
1539
  const out = draftJson(eng, { process: proc, session, draft: s.draft, openIn: s.presence.openIn, mine: s.mine.map((m) => ({ id: m.id, rev: m.rev, status: m.status, text: m.text })),
1514
- continueFrom: cf, checks: checksOf(cf.text), copies: s.copies, readAt: new Date(now).toISOString(), complete: s.problems.length === 0, problems: s.problems });
1540
+ continueFrom: cf, checks: ck.checks, copies: s.copies, readAt: new Date(now).toISOString(), complete: problems.length === 0, problems });
1515
1541
  return { out: JSON.stringify(out, null, 2), code: out.complete ? 0 : EXIT.partial };
1516
1542
  }
1517
1543
  /* save and propose: the input and every flag are checked before anything is read or written. */
@@ -1530,7 +1556,7 @@ async function draftMain(argv) {
1530
1556
  const base = PP.baseFor({ expect: val['--expect'], draft: s.draft, mine: s.mine, versions: s.versions, baseText, afterEarlier: argv.includes('--after-earlier') });
1531
1557
  if (base.missing) return draftFail(eng, 'missing-base', 'The text you started from isn\'t the draft, one of this session\'s proposals or a published version any more. Pass the text you read with --base, or read the draft again.');
1532
1558
  const operation = PP.newId(now, U.randomId(10)), at = new Date(now).toISOString();
1533
- const result = { process: proc, session, operation, outcome: null, why: null, link: base.link, rev: PP.sha(text), file: null, ids: PP.ids(base.text, text), checks: checksOf(text), warnings: [], history: null };
1559
+ const result = { process: proc, session, operation, outcome: null, why: null, link: base.link, rev: PP.sha(text), file: null, ids: PP.ids(base.text, text), checks: (await checksOf(text)).checks, warnings: [], history: null };
1534
1560
  if (result.ids.removed.length) result.warnings.push('This removes step or phase IDs: ' + result.ids.removed.join(', ') + '. Runs and skills that name them will need a look.');
1535
1561
  let why = sub === 'propose' ? 'asked' : s.presence.open ? 'open' : null;
1536
1562
  const draftNow = s.draft.exists ? s.draft.text : null, expectText = base.kind === 'none' || base.kind === 'version' ? null : base.text;