rvlib-shipkit 1.4.0 → 1.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.
- package/CHANGELOG.md +114 -0
- package/README.md +17 -17
- package/package.json +3 -2
- package/src/assets/gen/comfyts-gen.ts +12 -8
- package/src/assets/server/studio-server.ts +14 -2
- package/src/check.ts +73 -103
- package/src/checks/SK001-package-json-shape.check.ts +11 -6
- package/src/checks/SK002-contracts-fulfillness.check.ts +4 -0
- package/src/checks/SK003-claude-md-shape.check.ts +7 -3
- package/src/checks/SK004-gitignore-policy.check.ts +7 -4
- package/src/checks/SK005-rvlib-versions.check.ts +53 -21
- package/src/checks/SK006-npm-publish-safety.check.ts +4 -0
- package/src/checks/SK007-github-visibility.check.ts +4 -2
- package/src/checks/SK008-reviewer-agent.check.ts +6 -4
- package/src/checks/SK009-commit-ai-tells.check.ts +4 -2
- package/src/checks/SK010-undeclared-unit.check.ts +4 -0
- package/src/checks/SK011-sibling-unit-ranges.check.ts +4 -0
- package/src/checks/SK012-config-barrel-import.check.ts +5 -1
- package/src/checks/SK013-one-way-in-policy.check.ts +7 -3
- package/src/checks/SK014-prose-ste.check.ts +4 -0
- package/src/checks/SK015-nested-repo.check.ts +4 -2
- package/src/checks/SK016-legacy-check-shapes.check.ts +13 -12
- package/src/checks/SK017-vscode-hub-task.check.ts +22 -7
- package/src/checks/SK018-test-files-run.check.ts +6 -2
- package/src/checks/SK019-tests-run-in-units.check.ts +8 -4
- package/src/checks/SK020-self-spawning-check.check.ts +4 -0
- package/src/checks/SK022-machine-runs-the-pin.check.ts +6 -4
- package/src/checks/SK024-identity-colors.check.ts +5 -3
- package/src/checks/SK025-release-drift.check.ts +4 -5
- package/src/checks/SK026-asks-autoclose.check.ts +4 -4
- package/src/checks/SK027-agent-wiki-links.check.ts +4 -1
- package/src/checks/SK028-readme.check.ts +5 -1
- package/src/checks/SK029-explicit-check-files.check.ts +95 -0
- package/src/checks/SK030-cache-audit.check.ts +34 -0
- package/src/checks/SK031-trace-audit.check.ts +39 -0
- package/src/checks/SK032-ignored-inputs.check.ts +79 -0
- package/src/cli/cmd_Asks.ts +5 -5
- package/src/cli/cmd_Assets.ts +17 -13
- package/src/cli/cmd_Bump.ts +12 -11
- package/src/cli/cmd_CacheAudit.ts +43 -0
- package/src/cli/cmd_Check.ts +8 -7
- package/src/cli/cmd_Checks.ts +7 -6
- package/src/cli/cmd_Ci.ts +12 -11
- package/src/cli/cmd_Commands.ts +16 -13
- package/src/cli/cmd_ConfigKeys.ts +7 -6
- package/src/cli/cmd_Contracts.ts +3 -3
- package/src/cli/cmd_CrossRepoActivity.ts +8 -6
- package/src/cli/cmd_Fleet.ts +9 -7
- package/src/cli/cmd_GlobalProcesses.ts +120 -7
- package/src/cli/cmd_Home.ts +26 -19
- package/src/cli/cmd_Hooks.ts +6 -5
- package/src/cli/cmd_Init.ts +5 -4
- package/src/cli/cmd_Keys.ts +20 -8
- package/src/cli/cmd_Mv.ts +7 -6
- package/src/cli/cmd_Needs.ts +4 -3
- package/src/cli/cmd_Overview.ts +5 -4
- package/src/cli/cmd_Pb.ts +2 -2
- package/src/cli/cmd_Planned.ts +6 -5
- package/src/cli/cmd_Prose.ts +5 -5
- package/src/cli/cmd_Reflect.ts +7 -7
- package/src/cli/cmd_Release.ts +6 -5
- package/src/cli/cmd_Releases.ts +5 -4
- package/src/cli/cmd_Serve.ts +6 -4
- package/src/cli/cmd_Smoke.ts +5 -4
- package/src/cli/cmd_Start.ts +7 -6
- package/src/cli/cmd_Status.ts +4 -3
- package/src/cli/cmd_TraceAudit.ts +62 -0
- package/src/cli/cmd_Tui.ts +3 -2
- package/src/cli/cmd_When.ts +71 -0
- package/src/cli/cmd_Wiki.ts +4 -4
- package/src/cli.ts +11 -2
- package/src/collections/checks.ts +2 -0
- package/src/collections/collection-types.ts +12 -4
- package/src/collections/providers.ts +5 -3
- package/src/core/app-icon.ts +154 -0
- package/src/core/audit-report.ts +21 -0
- package/src/core/cache-audit.ts +72 -0
- package/src/core/canonical-scripts.ts +2 -2
- package/src/core/check-ctx.ts +14 -5
- package/src/core/check-runner.ts +43 -23
- package/src/core/config-rewrite.ts +37 -24
- package/src/core/discover-checks.ts +23 -2
- package/src/core/floor-resolve.ts +5 -7
- package/src/core/git-root.ts +13 -0
- package/src/core/ignored-block.ts +94 -0
- package/src/core/ignored-inputs.ts +47 -0
- package/src/core/jobs.ts +9 -2
- package/src/core/legacy-shapes.ts +39 -10
- package/src/core/one-lib.ts +59 -4
- package/src/core/one-way-in-policy.ts +2 -2
- package/src/core/outcome-words.ts +18 -2
- package/src/core/port-check-cli.ts +52 -0
- package/src/core/port-check-file.ts +295 -0
- package/src/core/port-check-files.ts +31 -0
- package/src/core/read-set.ts +97 -9
- package/src/core/retention.ts +0 -14
- package/src/core/run-check.ts +12 -9
- package/src/core/run-ci.ts +14 -5
- package/src/core/run-fix.ts +30 -23
- package/src/core/run-fixers.ts +48 -5
- package/src/core/runs-matrix.ts +179 -0
- package/src/core/runs.ts +242 -0
- package/src/core/trace-audit.ts +120 -0
- package/src/core/update-policy.ts +20 -0
- package/src/core/when-block.ts +29 -30
- package/src/core/when.ts +41 -103
- package/src/core/wire.ts +10 -14
- package/src/fleet/scan-fleet.ts +20 -4
- package/src/fleet/update-fleet.ts +6 -6
- package/src/home/gui-app.ts +24 -27
- package/src/home/home-launchd.ts +20 -4
- package/src/home/home-routes.ts +12 -1
- package/src/home/home-start.ts +11 -5
- package/src/home/hubs.ts +5 -5
- package/src/home/open-app.ts +2 -2
- package/src/home/pm.ts +8 -0
- package/src/home/run-pm.ts +1 -0
- package/src/home/serve-web.ts +2 -2
- package/src/home/supervise.ts +24 -1
- package/src/home/via-hub.ts +14 -0
- package/src/hooks/run-hooks.ts +48 -4
- package/src/init-repo.ts +6 -6
- package/src/ledger/db.ts +2 -13
- package/src/ledger/gate-checks.ts +133 -61
- package/src/ledger/git-key.ts +153 -31
- package/src/ledger/git-scan.ts +5 -2
- package/src/ledger/ledger.ts +136 -21
- package/src/ledger/store.ts +14 -0
- package/src/ledger/toolchain-identity.ts +248 -45
- package/src/plan/repo-source.ts +18 -39
- package/src/problems/extract.ts +3 -0
- package/src/release/run-release.ts +5 -0
- package/src/repo-config.ts +46 -54
- package/src/rules/rule.ts +2 -2
- package/src/scaffold/files/agent-coding.md +1 -1
- package/src/scaffold/files/agent-index.md +1 -1
- package/src/scaffold/files/verify-skill.md +2 -2
- package/src/serve/hub-app/activity-st.ts +5 -4
- package/src/serve/hub-app/activity-view.tsx +1 -1
- package/src/serve/hub-app/admin-svc.ts +36 -3
- package/src/serve/hub-app/admin-view.tsx +46 -12
- package/src/serve/hub-app/check-view.tsx +66 -46
- package/src/serve/hub-app/check-words.ts +1 -11
- package/src/serve/hub-app/checks-svc.ts +14 -35
- package/src/serve/hub-app/col-card.tsx +1 -44
- package/src/serve/hub-app/col-tables-view.tsx +84 -11
- package/src/serve/hub-app/col-tables.ts +27 -0
- package/src/serve/hub-app/cols-svc.ts +5 -4
- package/src/serve/hub-app/dock-st.ts +3 -2
- package/src/serve/hub-app/dock-view.tsx +36 -0
- package/src/serve/hub-app/dock.css +6 -0
- package/src/serve/hub-app/float-cards.tsx +30 -0
- package/src/serve/hub-app/floating.tsx +41 -4
- package/src/serve/hub-app/git-st.ts +42 -0
- package/src/serve/hub-app/git-view.tsx +61 -0
- package/src/serve/hub-app/hash-route.ts +45 -1
- package/src/serve/hub-app/header-bar.tsx +62 -72
- package/src/serve/hub-app/health-svc.ts +3 -2
- package/src/serve/hub-app/home-st.ts +14 -2
- package/src/serve/hub-app/home-strip.css +43 -12
- package/src/serve/hub-app/home-strip.tsx +115 -64
- package/src/serve/hub-app/home.html +1 -0
- package/src/serve/hub-app/hub-app.tsx +32 -8
- package/src/serve/hub-app/hub-types.ts +5 -2
- package/src/serve/hub-app/hub.css +89 -63
- package/src/serve/hub-app/icons.tsx +11 -0
- package/src/serve/hub-app/index.html +1 -0
- package/src/serve/hub-app/keys.ts +15 -4
- package/src/serve/hub-app/live-svc.ts +3 -2
- package/src/serve/hub-app/machine-st.ts +18 -22
- package/src/serve/hub-app/plan-view.tsx +51 -0
- package/src/serve/hub-app/planned-st.ts +9 -4
- package/src/serve/hub-app/processes-view.tsx +45 -0
- package/src/serve/hub-app/project-list.css +4 -1
- package/src/serve/hub-app/project-list.tsx +6 -3
- package/src/serve/hub-app/projects-svc.ts +25 -10
- package/src/serve/hub-app/prose-hits-st.ts +3 -2
- package/src/serve/hub-app/reach.ts +36 -0
- package/src/serve/hub-app/readme-svc.ts +3 -2
- package/src/serve/hub-app/repo-sections.ts +131 -0
- package/src/serve/hub-app/repo-st.ts +70 -14
- package/src/serve/hub-app/sidebar.tsx +17 -42
- package/src/serve/hub-app/timeline-svc.ts +3 -2
- package/src/serve/hub-app/tree-build.ts +151 -155
- package/src/serve/hub-app/tree-row.tsx +55 -12
- package/src/serve/hub-app/tree-svc.ts +97 -56
- package/src/serve/hub-app/when-view.tsx +89 -0
- package/src/serve/hub-app/when.css +17 -0
- package/src/serve/hub-runtime.ts +7 -18
- package/src/serve/project-gutter-route.ts +22 -1
- package/src/serve/run-serve.ts +1 -1
- package/src/serve/serve-hub.ts +4 -14
- package/src/suite/judge-suites.ts +6 -0
- package/src/trace/interpose.c +655 -0
- package/src/trace/read-trace.ts +485 -0
- package/src/trace/tracer.ts +282 -0
- package/src/serve/hub-app/processes-svc.ts +0 -63
- package/src/serve/hub-app/repo-sections.tsx +0 -61
- package/src/serve/repo-processes.ts +0 -89
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,117 @@
|
|
|
1
|
+
## 1.6.0
|
|
2
|
+
|
|
3
|
+
- **lint, typecheck, test and every task that declares nothing are cached now.** Such a task is keyed on the whole work tree, plus the gitignored paths the new `ignoredInputs` lists for it (`{ test: ['dist/**'] }`, `'*'` for every task; a path by content, `dir/**` by the names under it), and the lockfile, the installed packages, the bun and node versions and the toolkit code behind it. `sk trace-audit` runs each gate task once under a file tracer and names the gitignored paths it reads that its key misses. Tracing is on wherever it can run (Apple silicon macOS): SK031 audits about daily, `sk trace-audit --disable` opts a machine out and `SHIPKIT_TRACE=0` one run, and each finding joins the key of the task that read it at once, in every worktree of the repo. SK032 and the fix pass of `sk ci` then write the findings into their own block of `repo.config.ts`, once the repo pins 1.6.0 or later. The tracer never decides a verdict. It needs Apple silicon macOS: on Linux and in CI nothing audits, and SK030's daily sample is the only net there.
|
|
4
|
+
|
|
5
|
+
- **Every check that did not replay says why**, one line per reason: `ran: src/a.ts changed (lint, typecheck)`, `ran: nothing stored for it yet`, `ran, not kept: it reached the world through …`. A check its author declared never cached says so only under `--verbose`.
|
|
6
|
+
|
|
7
|
+
- **A cache key carries the code behind the answer, not the whole toolkit.** A check is keyed on its own module graph and the runner's, and a repo task on the toolkit modules the repo's config imports, so a toolkit edit re-runs only the checks it can change.
|
|
8
|
+
|
|
9
|
+
- **A command loads the image and comfy libraries only when it runs**: `sk --help` went from 540 ms to 220 ms.
|
|
10
|
+
|
|
11
|
+
- **SK030 audits the cache.** About once a day, while somebody works in the repo, it re-runs a sample of the checks the cache would answer right now, fresh, and compares. A cached green that a fresh run disagrees with fails, naming the check whose key misses a path its body reads, and the stale answer is dropped so it is never served again. `sk cache-audit` is the same audit on demand (`--sample <n>`, `--only <id>`, `--json`).
|
|
12
|
+
|
|
13
|
+
## Unreleased (patch)
|
|
14
|
+
|
|
15
|
+
- **The hub tree groups assets by kit category.** Under media, each category is a row that folds, and its label opens the category page. An asset row reads by its kit label (`mon/brindon/front`), never its file id.
|
|
16
|
+
|
|
17
|
+
- **SK001 no longer asks a comfy repo for a `comfy:up` script.** The box is a global process that `sk gp start comfyui` starts from any repo, so a per-repo script only copied that command. A `.shipkit/comfy/` folder now asks for nothing, and SK001 no longer probes it.
|
|
18
|
+
|
|
19
|
+
- **Every repo's tree has the same top rows.** A section the repo has nothing in (no process, no contract, no secret, no asks, no reflections, no tests, no media, no wiki) stays in the list, greyed, its title saying why. The processes row still opens its page, which says the repo declares none. The others open nothing, and the arrows step over them. Plan, git and admin still exist only on the home.
|
|
20
|
+
|
|
21
|
+
- **Every command starts faster.** A command that draws images or talks to ComfyUI loads canvas, sharp and comfy-ts when it runs, instead of every command loading them at startup (canvas alone read the system fonts). A fully cached `sk ci` drops from about 1.2s to 0.7s, `sk --help` from 540ms to 220ms.
|
|
22
|
+
|
|
23
|
+
- **Every check that did not come from the cache says why.** Above the verdict, one line per reason: `ran: src changed (lint, test)` names what moved since the last stored answer, `ran: nothing stored for it yet`, `ran, not kept: it reached the world through \`git check-ignore -q\`` for an answer that can never be stored, and `never cached: <reason>` for a check no key describes. A check that can never be kept was counted like a key that moved.
|
|
24
|
+
|
|
25
|
+
- **Every repo's media page loads its comfy workflows, not only the first one's.** comfy-ts keeps one instance per process, rooted at one repo, and the daemon serves every repo from one process: the second repo to load its workflows got "conflicts with the existing instance". The instance is now the first repo's, holding only the host caches every repo on that host shares, and each repo's drafts resolve under its own `.comfy-ts/drafts/`.
|
|
26
|
+
|
|
27
|
+
- **Editing shipkit no longer re-runs every check in every repo.** The cache key hashed the toolkit's whole source, and a repo that links it hashed the linked package again, tests included, so a css tweak of the hub or a new test re-ran every cached check everywhere. A key now carries the runner (the modules that run a check and store its answer, and the templates a check reads), the check's own module closure (modules it runs by path included), and for a check running the repo's code, the toolkit modules the repo imports. An edit no answer depends on moves no key, and `sk keys --why` lists the toolkit files behind a key.
|
|
28
|
+
|
|
29
|
+
- **⌘↑ and ⌘↓ open the repo above or below in the project list**, from any page, round both ends. In a text field they still move the caret. The list's foot hints them, and its sort button now just says sort, the order it is in moving to its title.
|
|
30
|
+
|
|
31
|
+
- **An app icon fills its tile, on the repo's colour.** The shipkit app and every process app drew the repo's mark as it was, transparent margin included, so a small mark was a dot on the dock. The icon now cuts the mark to its own box (faint alpha noise, which generated images carry over the whole canvas, counts as margin) and sets it on a rounded plate in the identity colour it stands out on most, on the macOS icon grid. A mark that already fills its box, a photo or a tile, becomes the plate. New colours rebuild the app at its next open.
|
|
32
|
+
|
|
33
|
+
- **The identity colours moved to the admin page, with a preview of the app icon.** The two pickers left the strip. The admin page opens on an identity card: the app icon drawn in the colours being picked (`/api/projects/app-icon/<repo>`, the same drawing the app build uses), and the repo name on a dark and on a light page. The card reads a repo by the name the project list knows, so a repo whose config names it otherwise (screenplay, named screenplay-shed) shows its own colours and icon.
|
|
34
|
+
|
|
35
|
+
- **A broken comfy setup no longer takes the whole media page down.** The workflows load only when a kind generates with comfy. When they fail to load, the media API still mounts: the rolls, the picks and every other kind stay served, and the reason shows in the page errors and in every comfy roll that fails.
|
|
36
|
+
|
|
37
|
+
- **`@napi-rs/canvas` and `comfy-ts` are one copy per process, like the toolkit.** A repo file that imports its own install gets the host copy. A brand that loaded an image with its own canvas copy and drew it on the toolkit's canvas failed ("Value is not one of these types"), and workflows written against an older comfy-ts failed to load.
|
|
38
|
+
|
|
39
|
+
- **A failing test no longer paints the last test file red.** Bun repeats every failure in a recap after the last file of its report, and the hub read those lines as failures of that file: the tests page and the problems pane blamed an innocent suite. The recap now closes the report.
|
|
40
|
+
|
|
41
|
+
- **The arrows walk the repo tree from anywhere on the page.** Up and down move to the next row and open its page at once, with no need to click the tree first. Left and right fold, unfold and climb as before. In the filter box, up and down walk the rows the filter keeps, and left and right stay the text caret's. With no cursor yet, they start from the section of the page shown. A branch with no page of its own only takes the cursor. A walk with the arrows is one history entry, so a held arrow never floods the browser history and back returns to where the walk started. While an asset is open, left and right stay the atelier's roll keys.
|
|
42
|
+
|
|
43
|
+
- **No repo page is a card grid.** Every collection without a table of its own (contracts, reflections, posts, workflows) is now a list, one line per item, red first. The processes page is the overview's process table for that repo, with the opened process's description and log under it. The tables sit on a panel box for contrast, and the page title and group headers are larger and in plain ink.
|
|
44
|
+
|
|
45
|
+
- **The secrets page is a list.** One line per secret, never a card: a dot, the name, missing, minor or present, and its sentence. Missing ones come first.
|
|
46
|
+
|
|
47
|
+
## 1.5.0
|
|
48
|
+
|
|
49
|
+
- **Every check declares where it runs and how it caches, with no default.** A check file carries `runs` (`onPreCommit`, `onCi`, `onFull`, `onRelease`, `onHealth`, `onSave`, `onClock`), `cache` (`files`, `reads`, `ttl`, `fact` or `never`, each with its one parameter), `fix` (`none`, `ci` or `manual`, the function itself is now `repair`) and `applies`. `check()` refuses a missing key, a retired key (`when`, `inputs`, `ttl`, `never`, `every`, `cron`, `whenInactive`, `planOff`, `cacheKey`) and the combinations that contradict each other. The gate, the fix pass, the release preflight and the planner read the declared axes; a release never applies a check repair. Every built-in carries its own placement: SK024, SK017 and SK028 run in the health pass only, and SK005, SK006, SK011, SK014, SK016 and SK022 are out of the quick gate. A check file that does not load is one failing row instead of a dead inventory.
|
|
50
|
+
|
|
51
|
+
- **SK029 ports a check file still in the retired shape.** Its repair writes `runs`, `cache`, `fix` and `applies` with the values the old keys resolved to, so the check runs where it ran, and the fix pass of `sk ci` applies it: a repo is carried across by its first gate.
|
|
52
|
+
|
|
53
|
+
- **A repo only turns a check off.** `off: { <id>: '<why>' }` in `repo.config.ts` replaces the `when` map; `defineRepo` refuses every `when` word but `off:<why>`, and SK029 ports that and `ruleExempt` into `off`. The hub's switch on a check page writes it.
|
|
54
|
+
|
|
55
|
+
- **The pre-commit hook runs the checks declaring `onPreCommit`**, in process on the working tree, under a budget of at most 100ms each; a failure refuses the commit. `sk hooks list` names them. The whole hook warns past 1s.
|
|
56
|
+
|
|
57
|
+
- **`sk when` and the when page show what runs when**: the legend of every context and value, then every check on every context. `--only onCi` keeps what the quick gate runs.
|
|
58
|
+
|
|
59
|
+
- **The hub tree has four sections where it had checks.** contracts (⌥K), shipkit checks (⌥S), repo checks (⌥C) and secrets (⌥X) are top rows, each with its own red count, and their rows sit straight under them, one indent less. shipkit checks and repo checks open the checks table showing only their own checks (`#page:checks:shipkit`, `#page:checks:repo`).
|
|
60
|
+
|
|
61
|
+
- **A check page lists where the check runs, one line per context.** Commit, ci, full, release, health, save, clock, fix and cache each show the declared value and what it means, in the words of the `sk when` legend. A context that does not run the check stays dim. The switch that turns the check off in this repo, with its reason, sits under that list.
|
|
62
|
+
|
|
63
|
+
- **The tree drops the gray ci, release, health word on a check row.** A check declares seven contexts now, which one word could not say. The row's tooltip lists every context that runs it (`runs on ci · full · release · health · save moved`), and the label gets the width back. A check that no context runs reads off.
|
|
64
|
+
|
|
65
|
+
- **A link button copies the page's url.** At the bottom right of the dock bar, it shows the url on hover and copies it on click. The app window has no url bar.
|
|
66
|
+
|
|
67
|
+
- **Fix: the repo tree's scrollbar is a thin bar.** In the macOS app it drew a wide dark band beside the tree; it is now a 6px thumb on a transparent track, and the rows sit closer to it.
|
|
68
|
+
|
|
69
|
+
- **The tree gives its width back to the labels.** A row's duration is a small clock now, tinted by the same tone table, with the figure in its tooltip. The indent per level and the space around the chevrons are a little smaller.
|
|
70
|
+
|
|
71
|
+
- **The command index is lean.** Every command's `description` is one short line (at most 100 characters, a test holds it), and the long text moved to `details`, which `--help` still prints. `shipkit commands` renders each signature with its flags inline, but no longer a line per flag, and every example uses `sk`, which is also the name `--help` shows. The generated index went from 33 KB to 17 KB.
|
|
72
|
+
|
|
73
|
+
- **`sk`, the short name.** The package ships a second bin, `sk`, the same cli as `shipkit`.
|
|
74
|
+
|
|
75
|
+
- **The fix pass is skipped over a tree it already left clean.** When HEAD, every uncommitted file, the toolchain and the fixer commands are the same as at the last pass that rewrote nothing, the formatter and import fixers do not run again. That was the whole wait before the first line of `shipkit ci`. CI never skips.
|
|
76
|
+
|
|
77
|
+
- **Every gate line says where its answer came from.** A check that ran ends its first line with `(ran 6.4s)`, one replayed from the cache with `(cached, 3h old, 6.4s saved)`, and a world check with `(ran 40ms, kept 1h more)` or `(cached, 2m old, kept 23h more)`. Before, a rule check that ran said nothing and a cached one never said how old its answer was.
|
|
78
|
+
|
|
79
|
+
- **Fix: a repo that `bun link`s a package replays from the cache.** A package linked outside the repo made every check "never cached", so a repo linking its toolchain re-ran everything on every gate. The link target is now keyed by its content (each file's path, size and bytes, symlinks followed, its own installs), so an edit there still moves the key and an untouched one replays. A linked tree that cannot be read keys nothing.
|
|
80
|
+
|
|
81
|
+
- **`shipkit gp start|stop|restart <id>`.** The overview's switches for a global process, from a terminal: pm takes the switch, and the command follows pm's status until the probe settles it. It exits 0 once the process is up (down for stop), and 1 with its note and its last log lines when it crashed or stayed down.
|
|
82
|
+
|
|
83
|
+
- **Widgets are asked for only by a visible overview.** The page polled `/api/home/widgets.json` from every page, hidden tabs and app windows included, so the daemon rendered every widget around the clock. A widget that samples another machine over ssh kept sampling it all day. A repo page or a hidden tab now asks for none, and the daemon stops rendering 30 s after the last read.
|
|
84
|
+
|
|
85
|
+
- **Removed: `serve.processes`.** A hub no longer supervises processes of its own: declare each one as a global process in a `<name>.shipkit.ts` file (`export default globalProcess({...})`), which pm runs. `defineRepo` refuses the old key and says so. The hub routes `/api/processes.json` and `/api/processes/act`, the header switches and the `process logs` retention store go with it.
|
|
86
|
+
|
|
87
|
+
- **A repo's plan, git and reflections are sections of its tree.** `plan` (⌥N) lists what the machine runs there and when, with run now. `git` (⌥G) shows the branch against its upstream, what is not committed and the commits of the week, with VS Code and Fork, from a new daemon route `/api/home/git.json?repo=`. `reflections` (⌥F) left the checks section for one of their own, one row each.
|
|
88
|
+
|
|
89
|
+
- **A repo's global processes are a section of its tree.** `processes` (⌥P) sits after `runs`, one row per process the repo declares, coloured by its state. The section opens a page with one card per process (its actions, its description as markdown, its log), and a row opens its own card, lit. A new resource kind, `process`, and a new address, `#page:processes[:<id>]`.
|
|
90
|
+
|
|
91
|
+
- **A repo page is not a box, each group is one.** A check, test, admin, runs or item page sits on the page background, and each group it holds (the facts, a titled section, the command, the admin rows, the runs list) is one lighter box.
|
|
92
|
+
|
|
93
|
+
- **A process menu shows its description as markdown, on the right.** The menu a process chip opens is two panels: its actions on the left, its `description` rendered as markdown on the right with its note and last log line, selectable, so a dev login written there is one click away. A process with nothing to say draws the actions alone.
|
|
94
|
+
|
|
95
|
+
- **Fix: a search reads every row, and a filtered tree says what it hides.** With the severity group on `needs` or `red`, a repo with few of them showed empty sections with no chevron and a search that found nothing. A search now ignores the group (`TreeSvc.visible`), and while the group hides rows a line under the tree counts them and shows all on a click (`TreeSvc.hidden`).
|
|
96
|
+
|
|
97
|
+
- **A process chip opens a menu instead of starting or stopping.** A click on a global process chip in the top bar opens its menu: start or stop, restart, open in a browser tab, in Chrome or as an app, its declared actions and take over. The chips sit right after the repo's name, and the spinner and `check…` sit at the right end before the editor icons.
|
|
98
|
+
|
|
99
|
+
- **The machine not answering is said once.** When the daemon, pm or the web does not answer, one card under the strip says so, with what hangs, what runs it and that the page comes back by itself; the daemon and pm down together share that card. The project list, the dock, a repo's trouble card and every repo pane keep what they last read and add no error line of their own. The bottom right toast is gone. `reach` and `ownTrouble` in `reach.ts` are the one reading of the web's tagged 502 and of a fetch with no answer. A route that fails while the daemon answers still shows its own error. The page stays drawn meanwhile: a repo keeps its last screen, since its hub's state is unknown rather than down, and the overview draws what it last had; a wait panel shows only when nothing was ever read.
|
|
100
|
+
|
|
101
|
+
- **Fix: pm kills a child that stopped answering, and its agent is never disk throttled.** A child whose pid lives but has not answered its probe for 90 s is hung: pm writes a WARN line to `pm.log`, sends SIGTERM then SIGKILL, and starts it again through the crash backoff (`hangMs` and `say` on a `ProcSpec`, `CHILD_HANG_MS`). A daemon had sat `starting` for an hour with its port open. The LaunchAgent now carries `ProcessType` Interactive: with none, launchd throttles the disk reads of pm and of every child it starts, and under a busy disk the daemon's boot reads of its sqlite store took minutes. Run `shipkit pm install` once to rewrite an installed agent. That install no longer strands the machine without pm: `launchctl bootout` returns before launchd lets go of the label, and the bootstrap right after it could fail with exit 5 once the old agent was gone. It is retried for 5 s (`BOOTSTRAP_TRIES`), and a final failure says pm is not loaded.
|
|
102
|
+
|
|
103
|
+
- **A readme section, and switching repo keeps the section open.** `readme` is the first row of a repo's left pane (⌥E, `#page:readme`). Selecting another repo opens the section the one you left showed; a repo already on a page of that section keeps it, and one without that section opens its README. `sectionOfSel` and `openSection` in `repo-sections.ts` light the rows and drive the switch, and `HashSel` gains `{ t: 'readme' }`.
|
|
104
|
+
|
|
105
|
+
- **A repo page's left pane is one tree whose top rows are its sections.** checks, runs, asks, tests, media, wikis and admin keep their icon, count, ⌥ key and lit state; a click opens the section's page, and only the chevron, the right arrow or a search unfolds it. Under them: the check origins with reflections, secrets and contracts under checks, one row per ask with the `done` archive, one row per test file, the assets with the workflow and post groups under media, one row per wiki. `KIND_SECTION` gives every resource kind one home, and a section the repo does not draw still gets a row when a resource lives in it (`buildTree` replaces `buildPathTree` and `buildTypeTree`). The by path and by type tabs, the `(?)` legend and the total are gone: each kind glyph names its kind in its tooltip, and the filter matches paths. The severity group sits beside the filter and narrows the section counts too. A search unfolds every section with a match and hides the rest; clearing it restores the folds. The planning line moved to the top of the checks page. `repo-sections.tsx` is `repo-sections.ts`.
|
|
106
|
+
|
|
107
|
+
- **The top bar carries no trouble text and no run buttons.** The hub's trouble and the machine's (a crashed child of pm, a crashed global process, a failed poll) are cards floating at the top right, just under the bar, full text, nothing drawn while all is well (`FloatCards`). The running command is a spinner with its words in a tooltip that opens at once, and nothing shows while idle. The health pass and the gate sit behind one small `check…` button, in a popover (`Pop` in floating.tsx) that Escape or a click outside closes. The standalone hub header gets the same.
|
|
108
|
+
|
|
109
|
+
- **The strip over a repo starts its global processes.** One chip per process the shown repo declares in its `.shipkit.ts` files: its dot and label, a click starts or stops it, and `↗` opens its url while it runs. pm runs them, so the chips show whether the repo's hub answers or not.
|
|
110
|
+
|
|
111
|
+
- **Fix: SK017 removes a `👁 TSC` watcher that runs `tsgo`.** The rule matched only a `tsc` binary, so a repo whose watcher had moved to tsgo kept it beside the gate's typecheck. A tsgo task under another label is still left alone. A wrapper that started only the TSC task goes with it, since it would open with the folder and start nothing.
|
|
112
|
+
|
|
113
|
+
- **Fix: a semver pin under a `bun link` moves when a release ships.** SK005 and `shipkit fleet` read the linked source, always the newest, so a repo pinned at 1.3.0 read current and a fresh clone kept installing 1.3.0. A pinned dep whose node_modules entry is a symlink is now judged by the pin's floor (`pinFloor`, `isLinkedInstall`). Behind, SK005 moves the pin through its guarded update, keeps its style, runs `bun link <name>` after it and FAILs when the link does not come back. The fleet row names it `pin 1.3.0 → 1.4.0 (linked, stale pin)`, `FleetDep` gains `linkedInstall` and `at`, and `fleet --update` reads the move off the pin (`judgedVersion`). A `link:` spec is judged as before.
|
|
114
|
+
|
|
1
115
|
## 1.4.0
|
|
2
116
|
|
|
3
117
|
- **Fix: `shipkit gui` wears the house mark from any directory.** The app took its icon from cwd, so a run outside every house repo rebuilt `Shipkit.app` with no icon. The house now comes from cwd, else from the installed pm agent's `WorkingDirectory` (`installedHouseRoot`, `guiRoot`), and never from cwd itself.
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ export default defineRepo({
|
|
|
18
18
|
})
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Everything the gate runs beyond the floor is a CHECK, and a check needs no config at all: drop a `<name>.check.ts` file anywhere and `
|
|
21
|
+
Everything the gate runs beyond the floor is a CHECK, and a check needs no config at all: drop a `<name>.check.ts` file anywhere and `sk ci`, `sk check`, `sk checks` and the hub discover it.
|
|
22
22
|
|
|
23
23
|
```ts
|
|
24
24
|
// e2e.check.ts
|
|
@@ -35,46 +35,46 @@ export default check({
|
|
|
35
35
|
## CLI
|
|
36
36
|
|
|
37
37
|
```
|
|
38
|
-
|
|
38
|
+
sk ci commit gate: floor (lint/typecheck/test/imports/cycles —
|
|
39
39
|
scripts win, imports/cycles/typecheck fill from bundled
|
|
40
40
|
tools with zero config, gaps print visibly) + contracts
|
|
41
41
|
+ your checks, parallel DAG; refreshes STATUS.md
|
|
42
42
|
[--bail --skip g --only id --json]
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
sk contracts release-contract fulfillness alone [--venue v --json]
|
|
44
|
+
sk check periodic health: the built-in checks + every *.check.ts in
|
|
45
45
|
the repo, auto-discovered + TTL-cached
|
|
46
46
|
[--json --fresh --frozen --fix [SK### …]]
|
|
47
47
|
--fix applies check autofixes then re-runs them; --frozen =
|
|
48
48
|
self-maintaining checks (rvlib self-update) report only
|
|
49
|
-
|
|
49
|
+
sk checks the inventory: every check, where it runs, blocks or warns,
|
|
50
50
|
and how it caches. --fixable = what repairs itself,
|
|
51
51
|
with its owning doc [--json --md --out]
|
|
52
|
-
|
|
52
|
+
sk tui live dashboard: build steps + every other check + panels
|
|
53
53
|
the repo injects via repo.config tui.panels
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
sk asks human action queue: list · ask "<title>" · asks check (auto-close)
|
|
55
|
+
sk needs everything blocked on a human, red/yellow by collection
|
|
56
56
|
report-only, exit 0 always [--json]
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
sk wiki serve|lint — live md link-graph + orphan/dangling-ref lint
|
|
58
|
+
sk mv rename an md page + its sibling dir, sweeping every ref
|
|
59
|
+
sk assets serve|fill|add|gen|identity|check — the asset atelier
|
|
60
60
|
(declare kinds in .shipkit/assets/kit.ts, roll, curate, ship)
|
|
61
|
-
|
|
61
|
+
sk pb collection <name> — scaffold a PocketBase collection's UI
|
|
62
62
|
folder (columns/card/grid/form/index) from the app's
|
|
63
63
|
generated pb types, create-only [--dry-run]
|
|
64
|
-
|
|
64
|
+
sk smoke browser pass over a DEPLOYED web app, read only: login
|
|
65
65
|
through the app's own form, every route in repo.config
|
|
66
66
|
`smoke`, screenshots, console/network problems, the host
|
|
67
67
|
quota headers, a detail page per `open` link. Exit code =
|
|
68
68
|
failed routes. Needs playwright in the repo
|
|
69
69
|
[--base <url> --routes /a,/b]
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
sk serve the repo's ONE server: hub page, wikis, studio, status
|
|
71
|
+
sk reflect your prompts across every Claude Code session of a repo:
|
|
72
72
|
all config roots, queued-mid-run ones included, harness
|
|
73
73
|
noise out, chronological md corpus for a retrospective
|
|
74
74
|
[repo…] [--short --out <dir> --json]
|
|
75
|
-
|
|
75
|
+
sk init scaffold config + scripts, print the resolved floor
|
|
76
76
|
[--bin <cli.ts path> for in-repo-source consumers]
|
|
77
|
-
|
|
77
|
+
sk start ensure <root>/<name> + its CLAUDE.md include; prints the dir
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
Output is a stable line protocol — `ok:` / `WARN` / `FAIL:` / `skip:` with indented `fix:` pointers — exit 0/1. Greppable by humans, parseable by agents, `--json` where structure helps.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rvlib-shipkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"funding": "https://github.com/sponsors/rvion",
|
|
5
5
|
"description": "All-in-one repo companion: ci gate (task DAG), auto-discovered health checks, release-contract fulfillness, asks queue, md-wiki tools, asset atelier, live tui",
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"CHANGELOG.md"
|
|
12
12
|
],
|
|
13
13
|
"bin": {
|
|
14
|
-
"shipkit": "./src/cli.ts"
|
|
14
|
+
"shipkit": "./src/cli.ts",
|
|
15
|
+
"sk": "./src/cli.ts"
|
|
15
16
|
},
|
|
16
17
|
"exports": {
|
|
17
18
|
".": "./src/shipkit.ts",
|
|
@@ -12,7 +12,8 @@ import { ComfyTS, type AnyVar, type ComfyExecution, type DefinedWorkflow, type M
|
|
|
12
12
|
export type GenProgress = { value: number; max: number; node: string | null }
|
|
13
13
|
export type OnProgress = (p: GenProgress) => void
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
/** root: the repo the workflow belongs to, which holds its drafts */
|
|
16
|
+
export type LoadedWorkflow = { id: string; root: string; file: string; wf: DefinedWorkflow }
|
|
16
17
|
export type ComfyTsRuntime = { workflows: Map<string, LoadedWorkflow>; heal?: string }
|
|
17
18
|
|
|
18
19
|
// function declaration, not const — TS only narrows on never-calls to
|
|
@@ -47,9 +48,11 @@ export async function loadCflowWorkflows(root: string, dir: string): Promise<Map
|
|
|
47
48
|
// explicit COMFY_TS_QUIET=0 from the caller.
|
|
48
49
|
process.env.COMFY_TS_QUIET ??= '1'
|
|
49
50
|
// before the first module import: a bare ComfyTS.create() inside the
|
|
50
|
-
// module then returns this same instance.
|
|
51
|
-
//
|
|
52
|
-
|
|
51
|
+
// module then returns this same instance. comfy-ts keeps ONE instance per
|
|
52
|
+
// process, and the daemon serves every repo from one process, so only the
|
|
53
|
+
// first repo roots it. What it roots is the host caches, shared by every
|
|
54
|
+
// repo on the same host; each repo's drafts resolve from its own root
|
|
55
|
+
if ((globalThis as { comfyts?: unknown }).comfyts == null) ComfyTS.create({ rootPath: root })
|
|
53
56
|
if (!existsSync(dir)) fail(`${dir}/ missing — ship at least one <id>.cflow.ts comfy-ts workflow module (copy from the rv-comfy template)`)
|
|
54
57
|
const out = new Map<string, LoadedWorkflow>()
|
|
55
58
|
for (const f of readdirSync(dir).filter((x) => x.endsWith('.cflow.ts')).sort()) {
|
|
@@ -65,7 +68,7 @@ export async function loadCflowWorkflows(root: string, dir: string): Promise<Map
|
|
|
65
68
|
const vars = varsOf(wf)
|
|
66
69
|
if (vars.prompt?.kind !== 'prompt' || vars.seed?.kind !== 'seed')
|
|
67
70
|
fail(`workflow '${id}' must declare vars.prompt (v.prompt) and vars.seed (v.seed) — shipkit drives both`)
|
|
68
|
-
out.set(id, { id, file, wf })
|
|
71
|
+
out.set(id, { id, root, file, wf })
|
|
69
72
|
}
|
|
70
73
|
if (out.size === 0) fail(`${dir}/ has no *.cflow.ts workflow modules — seed one from the rv-comfy template`)
|
|
71
74
|
return out
|
|
@@ -96,8 +99,9 @@ export function applyVarValues(wf: DefinedWorkflow, vars: Record<string, unknown
|
|
|
96
99
|
}
|
|
97
100
|
}
|
|
98
101
|
|
|
99
|
-
// ── drafts — comfy-ts disk format,
|
|
100
|
-
//
|
|
102
|
+
// ── drafts — comfy-ts disk format, .comfy-ts/drafts/<wfId>/<draftId>.json
|
|
103
|
+
// under the workflow's own repo, never the process instance's root (which is
|
|
104
|
+
// the first repo's in the daemon).
|
|
101
105
|
//
|
|
102
106
|
// The draft id is the KIND, never the asset: ONE `creature` draft behind all
|
|
103
107
|
// 107 creatures, so tuning the loras once tunes them for the whole kind. The
|
|
@@ -107,7 +111,7 @@ export function applyVarValues(wf: DefinedWorkflow, vars: Record<string, unknown
|
|
|
107
111
|
//
|
|
108
112
|
// Shipkit does its own IO here because comfy-ts exports no drafts API yet;
|
|
109
113
|
// swap when it lands.
|
|
110
|
-
const draftDir = (lw: LoadedWorkflow): string =>
|
|
114
|
+
const draftDir = (lw: LoadedWorkflow): string => join(lw.root, '.comfy-ts', 'drafts', lw.id)
|
|
111
115
|
export const draftPath = (lw: LoadedWorkflow, name: string): string => join(draftDir(lw), `${name}.json`)
|
|
112
116
|
|
|
113
117
|
export function readAssetDraft(lw: LoadedWorkflow, draftId: string): Record<string, unknown> | null {
|
|
@@ -49,7 +49,18 @@ export async function studioRoutes(deps: StudioDeps): Promise<StudioRoutes> {
|
|
|
49
49
|
const workspace = join(root, kit.workspace)
|
|
50
50
|
const dirOf = (id: string): string => assetDir(workspace, id)
|
|
51
51
|
let workflows: Map<string, LoadedWorkflow> | null = null
|
|
52
|
-
|
|
52
|
+
// only a comfy kind needs them, and a broken load costs only comfy
|
|
53
|
+
// generation: the rolls, the picks and every other kind stay served, and
|
|
54
|
+
// the reason rides in /api/state errors and in every comfy roll that fails
|
|
55
|
+
let comfyErr: string | null = null
|
|
56
|
+
if (kit.comfy != null && kit.kinds.some((k) => k.generate.backend === 'comfy')) {
|
|
57
|
+
try {
|
|
58
|
+
workflows = await loadCflowWorkflows(root, join(root, kit.comfy.workflows))
|
|
59
|
+
} catch (e) {
|
|
60
|
+
comfyErr = `comfy workflows not loaded: ${(e as Error).message}`
|
|
61
|
+
console.log(`WARN ${comfyErr}`)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
53
64
|
|
|
54
65
|
// custom panel scripts fail-fast here too (tests/embedders may bypass loadKit)
|
|
55
66
|
const missingPanels = missingPanelScripts(kit, root)
|
|
@@ -111,6 +122,7 @@ export async function studioRoutes(deps: StudioDeps): Promise<StudioRoutes> {
|
|
|
111
122
|
// workflows are pre-loaded above (fail-fast for a comfy studio); the
|
|
112
123
|
// resolver just hands the shared roll generator the ready runtime.
|
|
113
124
|
const resolveComfy = async (): Promise<ComfyTsRuntime> => {
|
|
125
|
+
if (comfyErr != null) throw new Error(comfyErr)
|
|
114
126
|
if (kit.comfy == null || workflows == null) throw new Error('kit has no comfy config — only canvas kinds can generate')
|
|
115
127
|
return { workflows, heal: kit.comfy.heal }
|
|
116
128
|
}
|
|
@@ -243,7 +255,7 @@ export async function studioRoutes(deps: StudioDeps): Promise<StudioRoutes> {
|
|
|
243
255
|
derived: parentIdsOf(f).length > 0, stale: await isStale(kit, workspace, f),
|
|
244
256
|
}
|
|
245
257
|
}))
|
|
246
|
-
return json({ ...queue.state, queued: queue.queuedCount, pending: queue.pendingIds(), errors: queue.errors.slice(-5), assets })
|
|
258
|
+
return json({ ...queue.state, queued: queue.queuedCount, pending: queue.pendingIds(), errors: [...(comfyErr == null ? [] : [comfyErr]), ...queue.errors.slice(-5)], assets })
|
|
247
259
|
}
|
|
248
260
|
|
|
249
261
|
if (pathname === '/api/asset') {
|
package/src/check.ts
CHANGED
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
// `cmd` argv, a subprocess, exit code is truth
|
|
15
15
|
//
|
|
16
16
|
// What a check declares about ITSELF is what every surface reads: `blocking`
|
|
17
|
-
// (can it stop you),
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
17
|
+
// (can it stop you), `runs` (every context it runs in), `cache` (how its
|
|
18
|
+
// answer is kept), `fix` (which command applies its repair), `applies` (does
|
|
19
|
+
// this repo give it work), `deps` (what must pass first). Every one of them is
|
|
20
|
+
// explicit, with no default (core/runs.ts). The file's own path joins its
|
|
21
|
+
// cache key automatically, so nothing declares its own code.
|
|
21
22
|
import type { CheckCtx } from './core/check-ctx.ts'
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import { FOREVER, parseDuration, type Line } from './core/wire.ts'
|
|
23
|
+
import { cacheProblem, combinationProblem, runsProblem, type Applies, type CacheDecl, type FixWhen, type Runs } from './core/runs.ts'
|
|
24
|
+
import { type Line } from './core/wire.ts'
|
|
25
25
|
import type { RepoConfig } from './repo-config.ts'
|
|
26
26
|
|
|
27
27
|
export { defineScan, mdPages, packageJson, type MdPage, type PackageJson, type Scan } from './scan.ts'
|
|
@@ -77,7 +77,7 @@ export type CheckDef = {
|
|
|
77
77
|
* gate asks "is my change good" and builds, `shipkit check` asks "is
|
|
78
78
|
* this repo healthy" and reports, and a tool chain has no business in
|
|
79
79
|
* the second question. A floor slot derives it, a file declares it, and
|
|
80
|
-
* it takes the 600s ceiling rather than the 120s one. `
|
|
80
|
+
* it takes the 600s ceiling rather than the 120s one. its `runs` say
|
|
81
81
|
* TIME (always, release, health, off). A build step on `health` is an
|
|
82
82
|
* opt in one: the gate parks it and `shipkit ci --with <id>` runs it. */
|
|
83
83
|
build?: boolean
|
|
@@ -85,74 +85,34 @@ export type CheckDef = {
|
|
|
85
85
|
* so a check naming none is a root of it, and a dep doubles as the race
|
|
86
86
|
* guard between two checks that touch one file. */
|
|
87
87
|
deps?: string[]
|
|
88
|
-
/** WHERE
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
|
|
106
|
-
/** a DECIDED never-cacheable, with the reason. Says somebody looked and
|
|
107
|
-
* no key can describe this, which takes it off the burn-down list. */
|
|
108
|
-
never?: string
|
|
88
|
+
/** WHERE IT RUNS, every context explicit, no default (core/runs.ts owns
|
|
89
|
+
* the meaning of each key and each value). A repo moves a check it does
|
|
90
|
+
* not own OFF with `off` in repo.config.ts, never elsewhere. */
|
|
91
|
+
runs: Runs
|
|
92
|
+
/** HOW ITS ANSWER IS KEPT, one variant, every parameter explicit:
|
|
93
|
+
* `files` (keyed on these paths), `reads` (keyed on what the body read),
|
|
94
|
+
* `ttl` (a world answer that keeps a span), `fact` (a decided value, once
|
|
95
|
+
* per key), `never` (with the reason). The file's own path joins every
|
|
96
|
+
* key automatically, so nothing declares its own code. */
|
|
97
|
+
cache: CacheDecl
|
|
98
|
+
/** which command applies `repair`: `'none'` (no repair), `'ci'` (the
|
|
99
|
+
* gate's fix pass and `shipkit check --fix`), `'manual'` (only
|
|
100
|
+
* `shipkit check --fix <id>`) */
|
|
101
|
+
fix: FixWhen
|
|
102
|
+
/** whether this repo gives the check anything to do: `'always'`, or a
|
|
103
|
+
* pure function of { root, config } returning the reason it does not
|
|
104
|
+
* (the row is then n/a in every context), or null */
|
|
105
|
+
applies: Applies
|
|
109
106
|
/** it REWRITES the repo while it runs (updates a dependency, untracks a
|
|
110
107
|
* file) unless the run is frozen, so a frozen run asks it a different
|
|
111
108
|
* question: only a check declaring this carries `frozen` in its cache
|
|
112
|
-
* key,
|
|
113
|
-
* a normal one. Declared, never guessed from the body. */
|
|
109
|
+
* key. Declared, never guessed from the body. */
|
|
114
110
|
writes?: boolean
|
|
115
|
-
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
|
|
120
|
-
* change only. Or a PURE function of its last result (`status`, `at`, `streak`
|
|
121
|
-
* of that same status) and whether the repo is active, returning any of
|
|
122
|
-
* those: `(last) => (last?.status === 'fail' ? '1h' : '7d')`. A lambda
|
|
123
|
-
* that throws is an error row on the planned page, and the check runs on
|
|
124
|
-
* change only. Absent: its `ttl`, else a build step or a check keyed on
|
|
125
|
-
* its files waits for a save. A repo check nothing can key runs about
|
|
126
|
-
* once a day until it declares one, and a built-in never may: `shipkit
|
|
127
|
-
* keys --burn-down` names both. */
|
|
128
|
-
every?: Every
|
|
129
|
-
/** a time of day or week for the rare check that needs one, as five cron
|
|
130
|
-
* fields in local time (`'0 9 * * 1'`, Mondays at 9). Never with `every`. */
|
|
131
|
-
cron?: string
|
|
132
|
-
/** what a due check does while its repo is inactive (no agent session in
|
|
133
|
-
* it, VS Code does not have it open, it is not flagged active): `'wait'`
|
|
134
|
-
* turns it late and runs it once the repo is active again, `'run'` runs
|
|
135
|
-
* it on time anyway (a credential probe, an api key that must keep
|
|
136
|
-
* working). A pure function of the last result may choose. Default
|
|
137
|
-
* `'wait'`. */
|
|
138
|
-
whenInactive?: WhenInactiveDecl
|
|
139
|
-
/** why this repo gives the check nothing to do on a clock, or null: the
|
|
140
|
-
* plan leaves it out there (no row, no timer), and a gate or a health
|
|
141
|
-
* pass still runs it. Read at every reload of the repo's checks, so a
|
|
142
|
-
* file that gives it work (an ask with a probe, a release unit) plans it
|
|
143
|
-
* again. Cheap and pure over the repo's files and config */
|
|
144
|
-
planOff?: (env: CheckEnv) => string | null
|
|
145
|
-
/** cache-identity contribution for a `ttl` check whose verdict depends on
|
|
146
|
-
* config: a changed key busts the answer instantly instead of serving
|
|
147
|
-
* a stale cached assertion for the rest of the TTL. */
|
|
148
|
-
cacheKey?: (env: CheckEnv) => string
|
|
149
|
-
/** idempotent auto-repair, never interactive. `shipkit check --fix <id>`
|
|
150
|
-
* applies it, then re-runs the check. It gets the same `c` as the body,
|
|
151
|
-
* writes with `c.write` and `c.remove`, and is never cached. An `fs`
|
|
152
|
-
* import in the check file would keep an undeclared body off its reads
|
|
153
|
-
* (core/raw-access.ts). A repair that needs
|
|
154
|
-
* judgement stays a FAIL with a pointer. */
|
|
155
|
-
fix?: (c: CheckCtx) => FixResult | Promise<FixResult>
|
|
111
|
+
/** idempotent auto-repair, never interactive, required when `fix` is not
|
|
112
|
+
* `'none'`. It gets the same `c` as the body, writes with `c.write` and
|
|
113
|
+
* `c.remove`, and is never cached. A repair that needs judgement stays a
|
|
114
|
+
* FAIL with a pointer. */
|
|
115
|
+
repair?: (c: CheckCtx) => FixResult | Promise<FixResult>
|
|
156
116
|
/** wall clock ceiling in ms. A `cmd` check is killed with its whole
|
|
157
117
|
* process tree at the ceiling. */
|
|
158
118
|
timeoutMs?: number
|
|
@@ -160,7 +120,7 @@ export type CheckDef = {
|
|
|
160
120
|
* passes over it prints a WARN and reads yellow, and never blocks.
|
|
161
121
|
* Judged on the wall time of a run that measured, at the quick tier.
|
|
162
122
|
* Default 2000 for an in process body, 10000 for a build step, a `cmd`
|
|
163
|
-
* or a world probe (
|
|
123
|
+
* or a world probe (cache `ttl`, `fact` or `never`, core/budget.ts).
|
|
164
124
|
* The repo's `budgets` wins over it. */
|
|
165
125
|
budgetMs?: number
|
|
166
126
|
/** a label several checks share, so `--skip <group>` parks all of them
|
|
@@ -198,6 +158,19 @@ export function describeFirst(...candidates: (string | null | undefined)[]): str
|
|
|
198
158
|
throw new Error(`no description candidate passes the shape: ${candidates.map((d) => JSON.stringify(d)).join(', ')}`)
|
|
199
159
|
}
|
|
200
160
|
|
|
161
|
+
/** the keys the explicit declaration replaced, and what replaces each */
|
|
162
|
+
const RETIRED_KEYS: Record<string, string> = {
|
|
163
|
+
when: 'runs (every context explicit)',
|
|
164
|
+
every: 'runs.onClock { every, inactive }',
|
|
165
|
+
cron: 'runs.onClock { every: <cron>, inactive }',
|
|
166
|
+
whenInactive: 'runs.onClock.inactive',
|
|
167
|
+
planOff: 'applies',
|
|
168
|
+
inputs: "cache { by: 'files', inputs, key }",
|
|
169
|
+
ttl: "cache { by: 'ttl', span, key } or { by: 'fact', key }",
|
|
170
|
+
never: "cache { by: 'never', why }",
|
|
171
|
+
cacheKey: 'cache.key',
|
|
172
|
+
}
|
|
173
|
+
|
|
201
174
|
/** THE way a check file declares itself. It validates the shape at import
|
|
202
175
|
* time, so a malformed check dies when discovery loads it and never mid
|
|
203
176
|
* gate. The id, the title and the owner are filled in by discovery, which
|
|
@@ -208,20 +181,21 @@ export function check(d: CheckDef): CheckDef {
|
|
|
208
181
|
const bad = descriptionProblem(d.description)
|
|
209
182
|
if (bad != null) throw new Error(`check${name}: ${bad}`)
|
|
210
183
|
if (typeof d.blocking !== 'boolean')
|
|
211
|
-
throw new Error(`check${
|
|
184
|
+
throw new Error(`check${name}: blocking is required — true (about this repo, it stops you) or false (about the world, it warns)`)
|
|
212
185
|
if (has('run') === has('cmd'))
|
|
213
|
-
throw new Error(`check${
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
186
|
+
throw new Error(`check${name}: declare exactly one body, \`run\` (in process) or \`cmd\` (argv)`)
|
|
187
|
+
const raw = d as unknown as Record<string, unknown>
|
|
188
|
+
const retired = Object.keys(RETIRED_KEYS).filter((k) => raw[k] !== undefined)
|
|
189
|
+
if (retired.length > 0) throw new Error(`check${name}: ${retired.map((k) => `\`${k}\` is retired, declare ${RETIRED_KEYS[k]}`).join('; ')} (build/shipkit/runs.md)`)
|
|
190
|
+
if (typeof raw.fix === 'function') throw new Error(`check${name}: \`fix\` is now the word 'none' | 'ci' | 'manual', and the function is \`repair\` (build/shipkit/runs.md)`)
|
|
191
|
+
const runs = runsProblem(d.runs)
|
|
192
|
+
if (runs != null) throw new Error(`check${name}: ${runs}`)
|
|
193
|
+
const cache = cacheProblem(d.cache)
|
|
194
|
+
if (cache != null) throw new Error(`check${name}: ${cache}`)
|
|
195
|
+
if (d.applies !== 'always' && typeof d.applies !== 'function')
|
|
196
|
+
throw new Error(`check${name}: applies is required: 'always', or a function of { root, config } returning why this repo gives it nothing to do, or null`)
|
|
197
|
+
const whole = combinationProblem({ runs: d.runs, cache: d.cache, fix: d.fix, repair: d.repair, build: d.build === true, body: has('run') ? 'run' : 'cmd', blocking: d.blocking })
|
|
198
|
+
if (whole != null) throw new Error(`check${name}: ${whole}`)
|
|
225
199
|
return d
|
|
226
200
|
}
|
|
227
201
|
|
|
@@ -233,7 +207,7 @@ export function check(d: CheckDef): CheckDef {
|
|
|
233
207
|
|
|
234
208
|
export type TaskDef = {
|
|
235
209
|
/** the name every surface uses: the gate line, `--only`, `--skip`, a
|
|
236
|
-
* `deps` entry,
|
|
210
|
+
* `deps` entry, an `off` key and the ledger row. Unique in the repo. */
|
|
237
211
|
id: string
|
|
238
212
|
/** one line stating what must be true (the shape: descriptionProblem).
|
|
239
213
|
* A floor slot and a unit test check get theirs from the
|
|
@@ -255,7 +229,7 @@ export type TaskDef = {
|
|
|
255
229
|
/** a label several tasks share, so `--skip <group>` parks all of them at
|
|
256
230
|
* once (a local bench, a slow integration tier). */
|
|
257
231
|
group?: string
|
|
258
|
-
/** DEPRECATED, read
|
|
232
|
+
/** DEPRECATED, read as every gate axis false: out of the gate until
|
|
259
233
|
* `--with <id>` names it. */
|
|
260
234
|
optIn?: boolean
|
|
261
235
|
/** wall clock ceiling in ms, default 600000 for a build step. One that
|
|
@@ -264,14 +238,11 @@ export type TaskDef = {
|
|
|
264
238
|
timeoutMs?: number
|
|
265
239
|
/** CONTENT population: the repo-relative paths this task reads. Declaring
|
|
266
240
|
* them is what makes it keyable (ledger/git-key.ts). Undeclared is the
|
|
267
|
-
* DEFAULT
|
|
268
|
-
*
|
|
269
|
-
* that costs correctness: the key cannot
|
|
270
|
-
* a cached answer comes back over a tree
|
|
271
|
-
*
|
|
272
|
-
* key by name and never by content, so a task reading a gitignored
|
|
273
|
-
* fixture, a built dir or a .env under declares with `['.']` exactly as
|
|
274
|
-
* it would with a narrow list. */
|
|
241
|
+
* DEFAULT: the task is keyed on the whole work tree plus the repo's
|
|
242
|
+
* `ignoredInputs` (build/shipkit/cache.md). A list MISSING a path
|
|
243
|
+
* the command reads is the one that costs correctness: the key cannot
|
|
244
|
+
* move when that path changes, so a cached answer comes back over a tree
|
|
245
|
+
* it no longer describes. */
|
|
275
246
|
inputs?: string[]
|
|
276
247
|
/** WORLD population: it reads the network, a keychain, $PATH or the clock,
|
|
277
248
|
* so nothing on disk describes its answer and a clean pass is remembered
|
|
@@ -333,13 +304,12 @@ export type Rule = {
|
|
|
333
304
|
|
|
334
305
|
// ── running a body, and what every line it prints carries ────────────────
|
|
335
306
|
|
|
336
|
-
/** the config part of a check's cache identity
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
return at.at === 'off' ? `${key ?? ''}|exempt:${at.reason ?? ''}` : `${key ?? ''}|when:${at.at}`
|
|
307
|
+
/** the config part of a check's cache identity: its declared key, and the
|
|
308
|
+
* reason it is off. Turning a check off, or editing why, asks the question
|
|
309
|
+
* again rather than serving the answer from before. Where it runs is not
|
|
310
|
+
* part of it: the answer to one question does not depend on who asks. */
|
|
311
|
+
export function checkCacheExtra(key: string | undefined, off: string | null): string | undefined {
|
|
312
|
+
return off == null ? key : `${key ?? ''}|exempt:${off}`
|
|
343
313
|
}
|
|
344
314
|
|
|
345
315
|
/** THE one way to run an in-process body (the check suite and the `--fix`
|