@joshuafolkken/kit 1.1010.0 → 1.1050.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/.claude/settings.json +1 -5
- package/.claude/skills/dependency-update/SKILL.md +6 -3
- package/.claude/skills/epic-commands/SKILL.md +46 -17
- package/.claude/skills/verify-ui/SKILL.md +1 -1
- package/.claude/skills/workflow-commands/SKILL.md +10 -25
- package/.claude/skills/workflow-commands/background-commands.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-park.md +6 -0
- package/.claude/skills/workflow-commands/backlogrun-progress.md +30 -36
- package/.claude/skills/workflow-commands/backlogrun-steps.md +16 -16
- package/.claude/skills/workflow-commands/backlogrun.md +3 -12
- package/.claude/skills/workflow-commands/chain-rule.md +2 -2
- package/.claude/skills/workflow-commands/followup-reference.md +2 -2
- package/.claude/skills/workflow-commands/fullrun-steps.md +4 -3
- package/.claude/skills/workflow-commands/fullrun.md +1 -1
- package/.claude/skills/workflow-commands/halfrun.md +2 -1
- package/.claude/skills/workflow-commands/into-target.md +1 -1
- package/.claude/skills/workflow-commands/issue-comments.md +1 -1
- package/.claude/skills/workflow-commands/observation-filing.md +80 -126
- package/.claude/skills/workflow-commands/pre-gate-cut.md +33 -36
- package/.claude/skills/workflow-commands/prerequisite.md +1 -1
- package/.claude/skills/workflow-commands/working-tree-hold.md +2 -2
- package/.codex/hooks.json +1 -5
- package/.github/workflows/auto-tag.yml +21 -0
- package/.github/workflows/publish.yml +9 -10
- package/.github/workflows/sonar-qube.yml +1 -1
- package/AGENTS.md +4 -25
- package/CLAUDE.md +24 -38
- package/GEMINI.md +4 -24
- package/README.md +22 -56
- package/dist/CLAUDE.md +24 -38
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-6PSTVOLH.js → chunk-5BK3K7V4.js} +114 -90
- package/dist/hooks/{chunk-PAXMLGSS.js → chunk-7VPDUBRN.js} +93 -175
- package/dist/hooks/{chunk-KPZN42RT.js → chunk-DMQN45FN.js} +3 -3
- package/dist/hooks/{chunk-WMIR5SNX.js → chunk-EJDX2TOB.js} +13 -3
- package/dist/hooks/{chunk-A2H5I2VD.js → chunk-GQ7KP45M.js} +2 -2
- package/dist/hooks/{chunk-4GDR2YTV.js → chunk-HJIISEAC.js} +4 -4
- package/dist/hooks/{chunk-AWTFKKFW.js → chunk-NAS4C7GP.js} +6 -33
- package/dist/hooks/{chunk-56LGMJJV.js → chunk-NJQOBE6L.js} +2 -2
- package/dist/hooks/{chunk-EZLHXKJ6.js → chunk-RLDX5SOC.js} +1222 -941
- package/dist/hooks/{chunk-GBJUERYK.js → chunk-WQ4OSBSS.js} +10 -3
- package/dist/hooks/codex-hook-adapter.js +10 -10
- package/dist/hooks/duplicate-read-guard.js +5 -5
- package/dist/hooks/format-edited.js +15 -34
- package/dist/hooks/investigation-guard.js +5 -5
- package/dist/hooks/pretool-guard.js +23 -16
- package/dist/hooks/stop-guard.js +21 -200
- package/dist/josh.js +42 -125
- package/dist/version/index.js +466 -42
- package/eslint/base.js +2 -11
- package/package.json +36 -10
- package/prompts/coding-standards.md +15 -290
- package/prompts/collaboration-workflow/cross-repo-epic.md +2 -51
- package/prompts/collaboration-workflow/epic-audit.md +2 -53
- package/prompts/collaboration-workflow/epic-bundle.md +2 -106
- package/prompts/collaboration-workflow/file-edits.md +6 -53
- package/prompts/collaboration-workflow/issue-template.md +4 -4
- package/prompts/collaboration-workflow/operating-rules.md +18 -75
- package/prompts/collaboration-workflow/principles.md +74 -0
- package/prompts/collaboration-workflow/residency.md +55 -15
- package/prompts/collaboration-workflow/rule-delivery.md +5 -46
- package/prompts/collaboration-workflow/shell-body.md +7 -9
- package/prompts/collaboration-workflow/turn-batching.md +21 -37
- package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
- package/prompts/collaboration-workflow/wip-cap.md +15 -18
- package/prompts/collaboration-workflow.md +22 -28
- package/prompts/sonar-hotspot-handling.md +1 -1
- package/scripts/adopt/adopt-logic.ts +2 -2
- package/scripts/adopt/adopt-toolkits.ts +3 -1
- package/scripts/adopt/adopt.ts +2 -2
- package/scripts/agent/agent-event.ts +2 -9
- package/scripts/agent/agent-exit-record.ts +2 -9
- package/scripts/auto-ok/auto-ok-cli.ts +9 -9
- package/scripts/backlog/backlog-budget-cli.ts +2 -6
- package/scripts/backlog/backlog-drive-cli.ts +20 -10
- package/scripts/backlog/backlog-drive.ts +50 -16
- package/scripts/backlog/backlog-next.ts +24 -8
- package/scripts/backlog/backlog-offer-cli.ts +6 -5
- package/scripts/backlog/backlog-plan-cli.ts +5 -1
- package/scripts/backlog/backlog-plan.ts +1 -1
- package/scripts/backlog/backlog-pool.ts +31 -11
- package/scripts/backlog/backlog-rank.ts +106 -0
- package/scripts/backlog/backlog-waves.ts +42 -29
- package/scripts/build/build-bin.ts +2 -1
- package/scripts/build/build-config-merge.ts +2 -1
- package/scripts/build/build-hooks.ts +31 -6
- package/scripts/build/build-managed-marker.ts +2 -1
- package/scripts/build/build-self-sync-guard.ts +2 -1
- package/scripts/build/build-version.ts +2 -1
- package/scripts/cases/cases-logic.ts +2 -6
- package/scripts/ci/pr-classification.ts +13 -11
- package/scripts/cost-runtime/cost-transcript.ts +4 -11
- package/scripts/delegation/delegation-policy.ts +11 -0
- package/scripts/doctor/doctor-consumer.ts +5 -12
- package/scripts/doctor/doctor-ports.ts +2 -2
- package/scripts/document/document-byte-budget.ts +13 -21
- package/scripts/document/document-section.ts +2 -6
- package/scripts/document/entry-read-budget.ts +6 -5
- package/scripts/{git/git-epic-add-body.ts → epic/epic-add-body.ts} +23 -23
- package/scripts/{git/git-epic-add-plan.ts → epic/epic-add-plan.ts} +23 -26
- package/scripts/{git/git-epic-add.ts → epic/epic-add.ts} +17 -17
- package/scripts/epic/epic-audit-cli.ts +4 -4
- package/scripts/epic/epic-audit-orphans.ts +2 -2
- package/scripts/epic/epic-audit-rationale.ts +1 -1
- package/scripts/epic/epic-audit.ts +1 -1
- package/scripts/{git/git-epic-body.ts → epic/epic-body.ts} +6 -6
- package/scripts/epic/epic-bundle-cli.ts +1 -1
- package/scripts/epic/epic-bundle.ts +2 -2
- package/scripts/epic/epic-candidate-confirm.ts +2 -1
- package/scripts/{git/git-epic-chains.ts → epic/epic-chains.ts} +4 -4
- package/{scripts-ai → scripts/epic}/epic-check.ts +5 -4
- package/{scripts-ai → scripts/epic}/epic-cli.ts +4 -4
- package/scripts/{git/git-epic-close-comment.ts → epic/epic-close-comment.ts} +9 -9
- package/scripts/{git/git-epic-close.ts → epic/epic-close.ts} +23 -19
- package/scripts/epic/epic-cross-repo.ts +21 -38
- package/scripts/{git/git-epic-decision.ts → epic/epic-decision.ts} +12 -14
- package/scripts/epic/epic-fetch.ts +3 -3
- package/scripts/epic/epic-graph.ts +3 -3
- package/scripts/epic/epic-index.ts +2 -2
- package/scripts/epic/epic-issue.ts +1 -1
- package/scripts/epic/epic-lane-offer.ts +11 -22
- package/scripts/epic/epic-next.ts +3 -3
- package/scripts/{git/git-epic-parse.ts → epic/epic-parse.ts} +3 -3
- package/scripts/{git/git-epic-promote.ts → epic/epic-promote.ts} +10 -10
- package/scripts/epic/epic-rank.ts +37 -0
- package/scripts/{git/git-epic-read.ts → epic/epic-read.ts} +9 -9
- package/scripts/{git/git-epic-reconcile.ts → epic/epic-reconcile.ts} +18 -20
- package/scripts/{git/git-epic-reference.ts → epic/epic-reference.ts} +4 -23
- package/scripts/epic/epic-relation-recheck.ts +4 -3
- package/scripts/{git/git-epic-relations.ts → epic/epic-relations.ts} +5 -5
- package/scripts/{git/git-epic-remove-plan.ts → epic/epic-remove-plan.ts} +17 -19
- package/scripts/{git/git-epic-remove.ts → epic/epic-remove.ts} +13 -13
- package/scripts/{git/git-epic-run.ts → epic/epic-run.ts} +27 -27
- package/scripts/{git/git-epic-sections.ts → epic/epic-sections.ts} +6 -6
- package/scripts/{git/git-epic-shape.ts → epic/epic-shape.ts} +7 -7
- package/scripts/epic/epic-solo.ts +29 -28
- package/scripts/{git/git-epic-validate.ts → epic/epic-validate.ts} +13 -13
- package/{scripts-ai → scripts/epic}/epic.ts +17 -16
- package/scripts/gh/fix-gh-packages.ts +2 -1
- package/scripts/gh/gh-spawn.ts +12 -10
- package/scripts/git/constants.ts +1 -1
- package/scripts/git/git-branch.ts +10 -1
- package/scripts/git/git-command.ts +2 -2
- package/scripts/git/git-error.ts +2 -5
- package/scripts/git/git-execa-error.ts +6 -1
- package/{scripts-ai → scripts/git}/git-followup-finish.ts +7 -7
- package/scripts/git/git-followup-flush.ts +74 -48
- package/scripts/git/git-followup-issue-close.ts +2 -2
- package/scripts/git/git-followup-label.ts +1 -1
- package/scripts/git/git-followup-pending.ts +1 -1
- package/{scripts-ai → scripts/git}/git-followup-workflow.ts +23 -11
- package/scripts/git/git-gh-exec.ts +85 -26
- package/scripts/git/git-gh-issue-read.ts +2 -2
- package/scripts/git/git-gh-issue-rest.ts +2 -2
- package/scripts/git/git-gh-issue-write.ts +6 -6
- package/scripts/git/git-gh-pr-read.ts +0 -21
- package/scripts/git/git-gh-pr.ts +2 -23
- package/scripts/git/git-next-issues.ts +34 -5
- package/scripts/git/git-pr-followup-wrapup.ts +2 -6
- package/scripts/git/git-preflight.ts +25 -10
- package/scripts/git/git-push-transport.ts +12 -69
- package/scripts/git/git-spawn.ts +79 -3
- package/scripts/git/git-ssh-keepalive.ts +74 -0
- package/scripts/git/git-staging.ts +21 -9
- package/{scripts-ai → scripts/git}/git-workflow.ts +11 -11
- package/scripts/git/github-issue-url.ts +1 -1
- package/scripts/git/gone-branch.ts +3 -2
- package/scripts/git/issue-labels.ts +13 -1
- package/scripts/git/issue-rank.ts +89 -0
- package/scripts/git/issue-reference.ts +19 -0
- package/scripts/git/main-merge.ts +2 -1
- package/scripts/git/main-sync.ts +3 -2
- package/scripts/git/schemas.ts +1 -1
- package/scripts/git/stash-pop-args.ts +7 -13
- package/scripts/git/stash-pop-cli.ts +2 -1
- package/{scripts-ai → scripts/git}/telegram-test-logic.ts +2 -2
- package/{scripts-ai → scripts/git}/telegram-test.ts +7 -7
- package/scripts/hooks/format-edited-cli.ts +15 -0
- package/scripts/hooks/format-edited-file.ts +2 -9
- package/scripts/hooks/format-rewrite-notice.ts +2 -8
- package/scripts/hooks/pretool-guard-cli.ts +21 -0
- package/scripts/hooks/pretool-guard.ts +14 -15
- package/scripts/init/init-ai-copy.ts +2 -5
- package/scripts/init/init.ts +8 -4
- package/scripts/init/start-exec.ts +7 -1
- package/scripts/init/start-plan.ts +18 -5
- package/scripts/init/start-setup-pr.ts +46 -19
- package/scripts/init/start-steps.ts +43 -10
- package/scripts/init/start.ts +42 -11
- package/scripts/issue/issue-cite.ts +19 -1
- package/scripts/issue/issue-comment-cli.ts +2 -1
- package/scripts/issue/issue-file-cli.ts +2 -1
- package/scripts/issue/issue-fold-cli.ts +9 -16
- package/scripts/issue/issue-scout-cli.ts +8 -13
- package/scripts/issue/measure-rerun-cli.ts +7 -6
- package/scripts/josh/josh-command-map.ts +0 -102
- package/scripts/josh/josh-commands-ai.ts +2 -2
- package/scripts/josh/josh-commands-backlog.ts +1 -1
- package/scripts/josh/josh-commands-development.ts +3 -3
- package/scripts/josh/josh-commands-project.ts +1 -1
- package/scripts/josh/josh-commands-workflow.ts +4 -4
- package/scripts/lane/lane-child-marker.ts +16 -2
- package/scripts/lane/lane-cli.ts +34 -15
- package/scripts/lane/lane-open.ts +3 -2
- package/scripts/lane/lane-registry.ts +3 -6
- package/scripts/lane/lane-report.ts +10 -4
- package/scripts/lib/cli-flags.ts +20 -2
- package/scripts/lib/error-message.ts +9 -0
- package/scripts/lib/read-file.ts +26 -2
- package/scripts/observations/observation-ledger-home.ts +98 -26
- package/scripts/observations/observation-ledger-migrate.ts +26 -21
- package/scripts/observations/observation-ledger-prepare.ts +10 -20
- package/scripts/observations/observation-ledger.ts +58 -25
- package/scripts/observations/observations-flush-cli.ts +6 -23
- package/scripts/observations/observations-flush-landing.ts +2 -1
- package/scripts/observations/observations-flush.ts +29 -20
- package/scripts/package/package-scout-cli.ts +8 -16
- package/scripts/ports/port-command.ts +2 -1
- package/scripts/propagate/propagate-plan.ts +185 -0
- package/scripts/propagate/propagate-publish.ts +6 -7
- package/scripts/propagate/propagate-run.ts +5 -1
- package/scripts/propagate/propagate-steps.ts +18 -2
- package/scripts/propagate/propagate-targets.ts +13 -2
- package/scripts/propagate/propagate.ts +63 -44
- package/scripts/refactor/import-graph.ts +2 -6
- package/scripts/registry-migration/migrate-logic.ts +1 -0
- package/scripts/registry-migration/migrate.ts +3 -2
- package/scripts/registry-migration/user-npmrc.ts +2 -2
- package/scripts/release/github-release.ts +1 -1
- package/scripts/release/release-cli.ts +2 -1
- package/scripts/repo/repo-setting.ts +10 -8
- package/scripts/review/review-attest.ts +44 -0
- package/scripts/review/review-brief-cli.ts +49 -2
- package/scripts/review/review-brief.ts +46 -1
- package/scripts/review/review-finding-ledger.ts +3 -3
- package/scripts/review/review-findings-cli.ts +4 -11
- package/scripts/review/review-record-cli.ts +14 -11
- package/scripts/review/review-record.ts +6 -16
- package/scripts/review/review-stamps.ts +7 -0
- package/scripts/rules/decision-oracle.ts +2 -2
- package/scripts/rules/implementation-cut.ts +1 -1
- package/scripts/rules/issue-fold.ts +1 -2
- package/scripts/rules/lane-carry-conflict.ts +1 -2
- package/scripts/rules/lane-park.ts +1 -2
- package/scripts/rules/pre-gate-cut.ts +2 -2
- package/scripts/rules/rule-body-guard.ts +2 -2
- package/scripts/rules/rule-value-cache.ts +99 -0
- package/scripts/rules/rule-value-cli.ts +10 -9
- package/scripts/rules/rule-value.ts +1 -1
- package/scripts/run/run-carry-args.ts +2 -6
- package/scripts/run/run-carry-cli.ts +5 -7
- package/scripts/run/run-cut-args.ts +7 -13
- package/scripts/run/run-ending-cli.ts +4 -7
- package/scripts/run/run-event-stream.ts +2 -5
- package/scripts/run/run-headless.ts +22 -6
- package/scripts/run/run-liveness-cli.ts +4 -7
- package/scripts/run/run-parent-cut-hook.ts +144 -0
- package/scripts/run/run-progress-args.ts +2 -6
- package/scripts/run/run-progress-cli.ts +2 -1
- package/scripts/run/run-progress-read.ts +2 -1
- package/scripts/run/run-progress.ts +21 -8
- package/scripts/run/run-ship-cli.ts +6 -74
- package/scripts/run/run-ship-preflight.ts +87 -0
- package/scripts/run/run-ship-review-steps.ts +5 -26
- package/scripts/run/run-ship-review.ts +9 -0
- package/scripts/run/run-ship-scoped.ts +44 -0
- package/scripts/run/run-ship-stage.ts +5 -1
- package/scripts/run/run-ship-steps.ts +105 -0
- package/scripts/run/run-ship.ts +2 -0
- package/scripts/run/run-tail-cli.ts +3 -3
- package/scripts/run/run-tidy-stashes.ts +7 -14
- package/scripts/run/run-wake-cli.ts +4 -8
- package/scripts/run/run-wake-describe.ts +19 -1
- package/scripts/run/run-wake-driver.ts +15 -2
- package/scripts/run/run-wake-session.ts +14 -4
- package/scripts/run/run-watcher-hook.ts +1 -1
- package/scripts/safe-chain/project-config.ts +3 -6
- package/scripts/security/security-audit-provision.ts +6 -7
- package/scripts/sonar/sonar-hotspots-cli.ts +2 -1
- package/scripts/sync/directory-copy-guard.ts +2 -1
- package/scripts/sync/sync-ai-files.ts +3 -3
- package/scripts/sync/template-source-logic.ts +3 -4
- package/scripts/test/e2e-retry-check.ts +3 -6
- package/scripts/time-runtime/time-bundle-call.ts +2 -2
- package/scripts/ui/ui-routes-cli.ts +3 -6
- package/scripts/version/bump-version.ts +2 -1
- package/scripts/version/npm-registry.ts +97 -0
- package/scripts/version/version-commands.ts +5 -4
- package/scripts/version/version-remote.ts +38 -33
- package/scripts/version/version-targets.ts +3 -3
- package/.claude/skills/workflow-commands/rule-residency.md +0 -267
- package/evals/scenarios/consult-not-execute.json +0 -20
- package/evals/scenarios/no-implicit-workflow.json +0 -26
- package/evals/scenarios/overrides-both-locations.json +0 -29
- package/evals/scenarios/upstream-interrupt.json +0 -23
- package/evals/scenarios/workflow-skill-loaded.json +0 -16
- package/prompts/agent-lint-protocol.json +0 -79
- package/prompts/agent-rules.md +0 -63
- package/prompts/collaboration-workflow/consultation-vs-execution.md +0 -8
- package/prompts/collaboration-workflow/distributed-docs.md +0 -7
- package/prompts/collaboration-workflow/durable-rules.md +0 -24
- package/prompts/collaboration-workflow/latest-first.md +0 -14
- package/prompts/collaboration-workflow/no-clones.md +0 -11
- package/prompts/collaboration-workflow/simplicity-first.md +0 -9
- package/prompts/collaboration-workflow/single-source-rules.md +0 -13
- package/scripts/build/shim-shell.ts +0 -25
- package/scripts/claude/skill-meta.ts +0 -37
- package/scripts/document/document-scan.ts +0 -108
- package/scripts/document/generate-catalog.ts +0 -69
- package/scripts/document/retired-phrases.ts +0 -143
- package/scripts/document/review-agent-type.ts +0 -46
- package/scripts/document/single-source.ts +0 -73
- package/scripts/git/git-fixture-workspace.ts +0 -88
- package/scripts/hooks/format-edited-file-fixtures.ts +0 -109
- package/scripts/hooks/prompt-hooks.ts +0 -38
- package/scripts/init/hook-command-bootstrap.ts +0 -83
- package/scripts/issue/issue-logic.ts +0 -50
- package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +0 -1227
- package/scripts/josh/josh-script-reader.ts +0 -11
- package/scripts/package/production-dependency-graph.ts +0 -68
- package/scripts/retrospective/retrospective-cli.ts +0 -97
- package/scripts/retrospective/retrospective.ts +0 -127
- package/scripts/rules/delivered-rules-harness.ts +0 -72
- package/scripts/rules/top-level-scripts-guard.ts +0 -47
- package/scripts/run/run-carry-flush.ts +0 -58
- package/scripts/run/run-transcript.golden.txt +0 -57
- package/scripts/run/run-wake-work.ts +0 -108
- package/scripts/sync/dependabot-pin-logic.ts +0 -135
- package/scripts/test/classify-isolation.ts +0 -149
- package/scripts/test/http-fault-injection.ts +0 -79
- package/scripts/test/josh-harness-environment.ts +0 -260
- package/scripts/test/josh-harness.ts +0 -96
- package/scripts/test/pilot-files.ts +0 -452
- package/scripts/test/pilot-measure.ts +0 -78
- package/scripts/test/test-network-guard.ts +0 -453
- package/scripts/test/test-repository-guard.ts +0 -317
- package/scripts/test/test-state-guard.ts +0 -85
- package/scripts/test/test-stdout-guard.ts +0 -54
- package/scripts/test/test-telegram-guard.ts +0 -90
- package/scripts/test/unit-guard-environment.ts +0 -12
- package/scripts/test/unit-projects.ts +0 -139
- package/scripts/test/vitest-include-globs.ts +0 -12
- package/scripts/time-runtime/time-batch-guard-fixtures.ts +0 -66
- package/scripts-ai/completion-gate.ts +0 -13
- package/scripts-ai/environment-loader.ts +0 -9
- /package/{scripts-ai → scripts/epic}/epic-cli-argv.ts +0 -0
- /package/{scripts-ai → scripts/epic}/epic-cli-remove.ts +0 -0
- /package/{scripts-ai → scripts/git}/followup-issue-number.ts +0 -0
|
@@ -1,29 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { OBSERVATION_LEDGER_PATH } from './observation-ledger'
|
|
1
|
+
import { observation_ledger_home } from './observation-ledger-home'
|
|
4
2
|
import { observation_ledger_line, type BrokenLine } from './observation-ledger-line'
|
|
5
|
-
import { observation_ledger_migrate } from './observation-ledger-migrate'
|
|
6
3
|
|
|
7
|
-
// **What has to hold before a ledger line is committed, asked by
|
|
8
|
-
// (joshuafolkken/kit#2763): `pnpm josh observations:flush
|
|
9
|
-
// `scripts/git/git-staging.ts
|
|
10
|
-
//
|
|
11
|
-
// the
|
|
4
|
+
// **What has to hold before a ledger line is committed, asked by every path that commits one**
|
|
5
|
+
// (joshuafolkken/kit#2763): `pnpm josh observations:flush`, the run's own commit in
|
|
6
|
+
// `scripts/git/git-staging.ts` and `pnpm josh followup`'s pre-merge ledger commit. Lines still on an
|
|
7
|
+
// old path are moved first (joshuafolkken/kit#2724, joshuafolkken/kit#2919), so the commit carries the
|
|
8
|
+
// move and the lines together, and every entry line is then parsed against the grammar
|
|
9
|
+
// (joshuafolkken/kit#2123) — each caller decides what a broken line costs it.
|
|
12
10
|
//
|
|
13
11
|
// **A ledger that is not there has no line to break** — the change a status saw is then its deletion,
|
|
14
12
|
// and a commit carries that like any other change to it.
|
|
15
|
-
async function read_ledger(root: string): Promise<string> {
|
|
16
|
-
try {
|
|
17
|
-
return await readFile(path.join(root, OBSERVATION_LEDGER_PATH), 'utf8')
|
|
18
|
-
} catch {
|
|
19
|
-
return ''
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
13
|
async function prepare(root: string): Promise<ReadonlyArray<BrokenLine>> {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
14
|
+
return observation_ledger_line.broken_ledger_lines(
|
|
15
|
+
(await observation_ledger_home.read(root)) ?? '',
|
|
16
|
+
)
|
|
27
17
|
}
|
|
28
18
|
|
|
29
19
|
const observation_ledger_prepare = { prepare }
|
|
@@ -1,22 +1,33 @@
|
|
|
1
|
-
// The observation ledger's one
|
|
2
|
-
// (joshuafolkken/kit#1756). `git-staging.ts` stages it with the run's own commit when its grammar
|
|
1
|
+
// The observation ledger's one location, named here rather than inside any of the places that act on
|
|
2
|
+
// it (joshuafolkken/kit#1756). `git-staging.ts` stages it with the run's own commit when its grammar
|
|
3
3
|
// holds (joshuafolkken/kit#2763), `hook-gate-reuse.ts` excludes it from what a hook calls a carried
|
|
4
4
|
// tree, and `observations-flush.ts` commits what no run carried — so a literal in each would be three
|
|
5
5
|
// answers to one question, and a rename reaching only some of them would silently split them.
|
|
6
6
|
//
|
|
7
7
|
// **It sits under `docs/maintainers/`, apart from the reader-facing documents** (joshuafolkken/kit#2724).
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
//
|
|
9
|
+
// **It is a directory of one file per issue, not one file** (joshuafolkken/kit#2919). One file every
|
|
10
|
+
// run appended to made two lanes' pull requests conflict at its tail, so lanes parked their lines in
|
|
11
|
+
// the primary checkout for a batch-end flush — and another run's stash took them from there, and the
|
|
12
|
+
// flush never saw them. A run now appends to `<issue>.md` in its own work tree and merges it with its
|
|
13
|
+
// own pull request: parallel lanes write different files, so nothing conflicts and nothing waits.
|
|
14
|
+
const OBSERVATION_LEDGER_DIRECTORY = 'docs/maintainers/observations'
|
|
15
|
+
|
|
16
|
+
const LEDGER_FILE_EXTENSION = '.md'
|
|
17
|
+
|
|
18
|
+
// What the single-file ledgers held is moved here (`observation-ledger-migrate.ts`), so no line is
|
|
19
|
+
// lost to the split.
|
|
20
|
+
const LEGACY_LEDGER_FILE = `legacy${LEDGER_FILE_EXTENSION}`
|
|
21
|
+
|
|
22
|
+
// Where the ledger lived as one file — `docs/observations.md` before joshuafolkken/kit#2724 and
|
|
23
|
+
// `docs/maintainers/observations.md` until joshuafolkken/kit#2919 — kept as migration sources rather
|
|
24
|
+
// than forgotten: a run still on the old code, a stash cut before the move and a consumer repository's
|
|
25
|
+
// existing ledger all write there, and `observation-ledger-migrate.ts` moves what it finds into the
|
|
26
|
+
// directory. Every place that recognizes the ledger recognizes them, so a line on either path is never
|
|
27
|
+
// staged into an ordinary commit. Remove them once every repository holding a ledger has migrated.
|
|
28
|
+
const LEGACY_OBSERVATION_LEDGER_PATHS: ReadonlyArray<string> = [
|
|
29
|
+
'docs/maintainers/observations.md',
|
|
30
|
+
'docs/observations.md',
|
|
20
31
|
]
|
|
21
32
|
|
|
22
33
|
// `git status --porcelain` prints `XY PATH`, so the path starts at the fourth character.
|
|
@@ -26,29 +37,48 @@ function status_path(line: string): string {
|
|
|
26
37
|
return line.slice(STATUS_PATH_INDEX).trim()
|
|
27
38
|
}
|
|
28
39
|
|
|
29
|
-
// The
|
|
40
|
+
// The ledger file one writer appends to — an issue number, or a date for a line written outside any
|
|
41
|
+
// issue's run (`observation-ledger-home.ts` decides which).
|
|
42
|
+
function ledger_file(name: number | string): string {
|
|
43
|
+
return `${OBSERVATION_LEDGER_DIRECTORY}/${String(name)}${LEDGER_FILE_EXTENSION}`
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function is_ledger_file_name(name: string): boolean {
|
|
47
|
+
return name.endsWith(LEDGER_FILE_EXTENSION)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// The suffix of the file a migration renames an old ledger to while it copies it
|
|
30
51
|
// (`observation-ledger-migrate.ts`): `docs/observations.md.<pid>.migrating`.
|
|
31
52
|
const MIGRATION_CLAIM_SUFFIX = '.migrating'
|
|
32
53
|
|
|
33
54
|
// A claim left by a process killed mid-migration holds ledger lines, so it is the ledger too: never
|
|
34
55
|
// staged by an ordinary commit, and absorbed by the next migration.
|
|
35
56
|
function is_migration_claim(file_path: string): boolean {
|
|
36
|
-
return (
|
|
37
|
-
file_path.startsWith(`${
|
|
38
|
-
file_path.endsWith(MIGRATION_CLAIM_SUFFIX)
|
|
57
|
+
return LEGACY_OBSERVATION_LEDGER_PATHS.some(
|
|
58
|
+
(legacy) => file_path.startsWith(`${legacy}.`) && file_path.endsWith(MIGRATION_CLAIM_SUFFIX),
|
|
39
59
|
)
|
|
40
60
|
}
|
|
41
61
|
|
|
62
|
+
// Porcelain status collapses an untracked directory to `docs/maintainers/observations/`, so the
|
|
63
|
+
// directory itself, trailing slash and all, is the ledger as much as any file inside it.
|
|
64
|
+
function is_in_directory(file_path: string): boolean {
|
|
65
|
+
return file_path.startsWith(`${OBSERVATION_LEDGER_DIRECTORY}/`)
|
|
66
|
+
}
|
|
67
|
+
|
|
42
68
|
function is_ledger_path(file_path: string): boolean {
|
|
43
|
-
return
|
|
69
|
+
return (
|
|
70
|
+
is_in_directory(file_path) ||
|
|
71
|
+
LEGACY_OBSERVATION_LEDGER_PATHS.includes(file_path) ||
|
|
72
|
+
is_migration_claim(file_path)
|
|
73
|
+
)
|
|
44
74
|
}
|
|
45
75
|
|
|
46
76
|
function is_ledger_line(line: string): boolean {
|
|
47
77
|
return is_ledger_path(status_path(line))
|
|
48
78
|
}
|
|
49
79
|
|
|
50
|
-
// The ledger paths a porcelain status names — what a
|
|
51
|
-
//
|
|
80
|
+
// The ledger paths a porcelain status names — what a commit of the ledger stages, which may be a new
|
|
81
|
+
// file together with an old one's deletion.
|
|
52
82
|
function ledger_paths(status_output: string): ReadonlyArray<string> {
|
|
53
83
|
return status_output
|
|
54
84
|
.split('\n')
|
|
@@ -57,7 +87,7 @@ function ledger_paths(status_output: string): ReadonlyArray<string> {
|
|
|
57
87
|
}
|
|
58
88
|
|
|
59
89
|
// **The one place that answers "does this working tree hold a pending observation append?"**
|
|
60
|
-
// `observations-flush.ts`'s `has_ledger_change` and `pnpm josh followup`'s pre-
|
|
90
|
+
// `observations-flush.ts`'s `has_ledger_change` and `pnpm josh followup`'s pre-merge ledger commit
|
|
61
91
|
// both ask it (joshuafolkken/kit#1810), so a literal in each would be the clone the header above
|
|
62
92
|
// warns against. A blank porcelain line carries no ledger path, so `is_ledger_line` answers false for
|
|
63
93
|
// it and no filtering is needed.
|
|
@@ -67,17 +97,20 @@ function has_pending_append(status_output: string): boolean {
|
|
|
67
97
|
|
|
68
98
|
const observation_ledger = {
|
|
69
99
|
has_pending_append,
|
|
100
|
+
is_ledger_file_name,
|
|
70
101
|
is_ledger_line,
|
|
71
102
|
is_ledger_path,
|
|
72
103
|
is_migration_claim,
|
|
104
|
+
ledger_file,
|
|
73
105
|
ledger_paths,
|
|
74
106
|
status_path,
|
|
75
107
|
}
|
|
76
108
|
|
|
77
109
|
export {
|
|
78
|
-
|
|
110
|
+
LEDGER_FILE_EXTENSION,
|
|
111
|
+
LEGACY_LEDGER_FILE,
|
|
112
|
+
LEGACY_OBSERVATION_LEDGER_PATHS,
|
|
79
113
|
MIGRATION_CLAIM_SUFFIX,
|
|
80
|
-
|
|
81
|
-
OBSERVATION_LEDGER_PATHS,
|
|
114
|
+
OBSERVATION_LEDGER_DIRECTORY,
|
|
82
115
|
observation_ledger,
|
|
83
116
|
}
|
|
@@ -1,38 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import {
|
|
3
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
4
4
|
import { observations_flush } from './observations-flush'
|
|
5
5
|
|
|
6
6
|
const FAILURE_EXIT_CODE = 1
|
|
7
7
|
|
|
8
|
-
function message_of(error: unknown): string {
|
|
9
|
-
return error instanceof Error ? error.message : String(error)
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// **From a lane, the flush acts on the primary checkout** (joshuafolkken/kit#2419). That is where the
|
|
13
|
-
// ledger lives (`observation-ledger-home.ts`), and a lane's own checkout is never the one a flush may
|
|
14
|
-
// switch — so the process moves there first, and every refusal after it is about that checkout, which
|
|
15
|
-
// the prefix names so a "run `pnpm josh ms` first" is read as advice for the primary checkout.
|
|
16
|
-
function enter_ledger_home(): string {
|
|
17
|
-
if (!observation_ledger_home.is_lane()) return ''
|
|
18
|
-
|
|
19
|
-
const root = observation_ledger_home.ledger_root()
|
|
20
|
-
|
|
21
|
-
process.chdir(root)
|
|
22
|
-
|
|
23
|
-
return `The observation ledger lives in the primary checkout, so this flush acts on ${root}: `
|
|
24
|
-
}
|
|
25
|
-
|
|
26
8
|
// The refusals are the command's whole safety story, so they are printed as the message rather than
|
|
27
9
|
// as a stack: "this checkout is on a feature branch" and "the tree holds somebody else's work" are
|
|
28
10
|
// both things the person fixes in one step, and a trace hides the sentence that says which.
|
|
11
|
+
//
|
|
12
|
+
// **It acts on the checkout it runs in** (joshuafolkken/kit#2919). A lane's ledger lines merge with
|
|
13
|
+
// the lane's own pull request, so a lane has nothing to flush and is refused like any feature branch.
|
|
29
14
|
async function main(): Promise<void> {
|
|
30
|
-
const prefix = enter_ledger_home()
|
|
31
|
-
|
|
32
15
|
try {
|
|
33
|
-
console.info(
|
|
16
|
+
console.info(await observations_flush.flush(new Date()))
|
|
34
17
|
} catch (error) {
|
|
35
|
-
console.error(
|
|
18
|
+
console.error(error_text.message_of(error))
|
|
36
19
|
process.exit(FAILURE_EXIT_CODE)
|
|
37
20
|
}
|
|
38
21
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
2
2
|
import type { OpenPull } from '#scripts/git/git-gh-pr-auto-merge'
|
|
3
3
|
import { git_pr_checks } from '#scripts/git/git-pr-checks'
|
|
4
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
4
5
|
|
|
5
6
|
// **A flush pull request lands whether or not the flush that opened it is still alive**
|
|
6
7
|
// (joshuafolkken/kit#2497). The merge used to happen only inside the flush's own wait, so a session
|
|
@@ -25,7 +26,7 @@ async function request_auto_merge(branch_name: string): Promise<boolean> {
|
|
|
25
26
|
|
|
26
27
|
return true
|
|
27
28
|
} catch (error) {
|
|
28
|
-
console.warn(AUTO_MERGE_WARNING,
|
|
29
|
+
console.warn(AUTO_MERGE_WARNING, error_text.message_of(error))
|
|
29
30
|
|
|
30
31
|
return false
|
|
31
32
|
}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { git_command } from '#scripts/git/git-command'
|
|
2
2
|
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
3
3
|
import { main_sync } from '#scripts/git/main-sync'
|
|
4
|
-
import {
|
|
4
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
5
|
+
import { observation_ledger, OBSERVATION_LEDGER_DIRECTORY } from './observation-ledger'
|
|
5
6
|
import type { BrokenLine } from './observation-ledger-line'
|
|
6
7
|
import { observation_ledger_prepare } from './observation-ledger-prepare'
|
|
7
8
|
import { observations_flush_landing } from './observations-flush-landing'
|
|
8
9
|
|
|
9
|
-
// The commit path for the ledger lines no run's own commit carried (joshuafolkken/kit#1756).
|
|
10
|
-
//
|
|
11
|
-
// git-staging.ts`, joshuafolkken/kit#2763),
|
|
12
|
-
//
|
|
10
|
+
// The commit path for the ledger lines no run's own commit carried (joshuafolkken/kit#1756). Every run —
|
|
11
|
+
// a lane's included — commits its appended lines with its own pull request (`scripts/git/
|
|
12
|
+
// git-staging.ts`, joshuafolkken/kit#2763, joshuafolkken/kit#2919), and `pnpm josh followup` commits a
|
|
13
|
+
// line appended after that commit before it merges, so what is left here is a line written on the
|
|
14
|
+
// default branch outside any issue's run — the date-named file `observation-ledger-home.ts` resolves.
|
|
13
15
|
//
|
|
14
16
|
// **The shape is `scripts/release/release-publish.ts`'s, deliberately.** Both open a pull request of
|
|
15
17
|
// their own over one path, wait on the same required checks every other pull request waits on, and
|
|
@@ -19,7 +21,7 @@ import { observations_flush_landing } from './observations-flush-landing'
|
|
|
19
21
|
// below does not survive — `observations-flush-landing.ts` carries why.
|
|
20
22
|
|
|
21
23
|
const COMMIT_MESSAGE = 'Record observation ledger entries'
|
|
22
|
-
const CLEAN_MESSAGE = `clean — ${
|
|
24
|
+
const CLEAN_MESSAGE = `clean — ${OBSERVATION_LEDGER_DIRECTORY} matches the commit it sits on, so there is nothing to flush`
|
|
23
25
|
const BRANCH_PREFIX = 'observations/'
|
|
24
26
|
const SUCCESS_EXIT_CODE = 0
|
|
25
27
|
const RETURN_FAILURE_MESSAGE =
|
|
@@ -55,7 +57,7 @@ function branch_name_for(stamp: string): string {
|
|
|
55
57
|
// single issue, and the ledger's own lines say where each one was seen.
|
|
56
58
|
function pull_request_body(): string {
|
|
57
59
|
return [
|
|
58
|
-
`Appended observations from \`${
|
|
60
|
+
`Appended observations from \`${OBSERVATION_LEDGER_DIRECTORY}\` that no run's own commit carried.`,
|
|
59
61
|
'',
|
|
60
62
|
'The ledger is append-only, and a second line under one key is what promotes an observation to an issue — so this pull request adds lines and changes none.',
|
|
61
63
|
'',
|
|
@@ -82,10 +84,6 @@ function other_changed_paths(status_output: string): ReadonlyArray<string> {
|
|
|
82
84
|
)
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
function message_of(error: unknown): string {
|
|
86
|
-
return error instanceof Error ? error.message : String(error)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
87
|
// **The branch a failed flush leaves behind holds the only copy of those lines**, because the working
|
|
90
88
|
// tree no longer shows them once they are committed. Sending a person to `pnpm josh ms` here — which
|
|
91
89
|
// is what the ordinary off-default refusal says — would abandon them without a word, so this branch
|
|
@@ -205,7 +203,9 @@ async function catch_up_default(default_branch: string): Promise<void> {
|
|
|
205
203
|
try {
|
|
206
204
|
await git_command.merge_fast_forward(default_branch)
|
|
207
205
|
} catch (error) {
|
|
208
|
-
throw new Error(behind_default_message(default_branch, message_of(error)), {
|
|
206
|
+
throw new Error(behind_default_message(default_branch, error_text.message_of(error)), {
|
|
207
|
+
cause: error,
|
|
208
|
+
})
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
|
|
@@ -242,7 +242,9 @@ async function return_to_default_branch(default_branch: string): Promise<void> {
|
|
|
242
242
|
try {
|
|
243
243
|
await git_command.fast_forward_local(default_branch)
|
|
244
244
|
} catch (error) {
|
|
245
|
-
throw new Error(`${FAST_FORWARD_FAILURE_MESSAGE} ${message_of(error)}`, {
|
|
245
|
+
throw new Error(`${FAST_FORWARD_FAILURE_MESSAGE} ${error_text.message_of(error)}`, {
|
|
246
|
+
cause: error,
|
|
247
|
+
})
|
|
246
248
|
}
|
|
247
249
|
|
|
248
250
|
const exit_code = await main_sync.run([])
|
|
@@ -258,7 +260,9 @@ async function push_and_open(branch_name: string): Promise<string> {
|
|
|
258
260
|
|
|
259
261
|
return await git_gh_command.pr_create(COMMIT_MESSAGE, pull_request_body(), 'ignore-for-release')
|
|
260
262
|
} catch (error) {
|
|
261
|
-
throw new Error(stranded_branch_message(branch_name, message_of(error)), {
|
|
263
|
+
throw new Error(stranded_branch_message(branch_name, error_text.message_of(error)), {
|
|
264
|
+
cause: error,
|
|
265
|
+
})
|
|
262
266
|
}
|
|
263
267
|
}
|
|
264
268
|
|
|
@@ -279,7 +283,7 @@ async function roll_back(branch_name: string, default_branch: string): Promise<s
|
|
|
279
283
|
|
|
280
284
|
return undefined
|
|
281
285
|
} catch (error) {
|
|
282
|
-
return message_of(error)
|
|
286
|
+
return error_text.message_of(error)
|
|
283
287
|
}
|
|
284
288
|
}
|
|
285
289
|
|
|
@@ -315,9 +319,12 @@ async function commit_ledger(branch_name: string, default_branch: string): Promi
|
|
|
315
319
|
} catch (error) {
|
|
316
320
|
const roll_back_reason = await roll_back(branch_name, default_branch)
|
|
317
321
|
|
|
318
|
-
throw new Error(
|
|
319
|
-
|
|
320
|
-
|
|
322
|
+
throw new Error(
|
|
323
|
+
rejection_message(branch_name, error_text.message_of(error), roll_back_reason),
|
|
324
|
+
{
|
|
325
|
+
cause: error,
|
|
326
|
+
},
|
|
327
|
+
)
|
|
321
328
|
}
|
|
322
329
|
}
|
|
323
330
|
|
|
@@ -345,7 +352,9 @@ async function land(
|
|
|
345
352
|
try {
|
|
346
353
|
await observations_flush_landing.wait_for_landing(branch_name, is_auto_merge)
|
|
347
354
|
} catch (error) {
|
|
348
|
-
throw new Error(stranded_branch_message(branch_name, message_of(error)), {
|
|
355
|
+
throw new Error(stranded_branch_message(branch_name, error_text.message_of(error)), {
|
|
356
|
+
cause: error,
|
|
357
|
+
})
|
|
349
358
|
}
|
|
350
359
|
|
|
351
360
|
await return_to_default_branch(default_branch)
|
|
@@ -366,7 +375,7 @@ function merged_message(branch_name: string): string {
|
|
|
366
375
|
function broken_lines_message(broken: ReadonlyArray<BrokenLine>): string {
|
|
367
376
|
const rows = broken.map((entry) => ` ${entry.line}\n ↳ ${entry.reason}`).join('\n')
|
|
368
377
|
|
|
369
|
-
return `\`${
|
|
378
|
+
return `\`${OBSERVATION_LEDGER_DIRECTORY}\` has ${String(broken.length)} line(s) that break the ledger grammar, so the ledger commit stops rather than carrying them:\n${rows}`
|
|
370
379
|
}
|
|
371
380
|
|
|
372
381
|
// **A malformed line is refused before a branch is cut, not after.** The ledger path was collected
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import {
|
|
3
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
4
4
|
import { z } from 'zod'
|
|
5
5
|
import {
|
|
6
6
|
package_scout,
|
|
@@ -120,27 +120,19 @@ function parse_size(raw: string | undefined): number {
|
|
|
120
120
|
return raw === undefined || !Number.isSafeInteger(parsed) || parsed <= 0 ? DEFAULT_SIZE : parsed
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
// An unknown flag is `undefined` rather than a throw, so the answer is the usage line rather than a
|
|
124
|
+
// stack trace, on a command whose output a workflow reads.
|
|
125
|
+
function read_arguments(argv: ReadonlyArray<string>): ScoutArguments | undefined {
|
|
126
|
+
const parsed = cli_flags.parse_or_undefined({
|
|
125
127
|
args: [...argv],
|
|
126
128
|
options: { size: { type: 'string' } },
|
|
127
129
|
allowPositionals: true,
|
|
128
130
|
})
|
|
129
|
-
const keywords = positionals.join(' ').trim()
|
|
130
|
-
|
|
131
|
-
if (keywords === '') return undefined
|
|
131
|
+
const keywords = parsed?.positionals.join(' ').trim() ?? ''
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
}
|
|
133
|
+
if (parsed === undefined || keywords === '') return undefined
|
|
135
134
|
|
|
136
|
-
|
|
137
|
-
// trace, on a command whose output a workflow reads.
|
|
138
|
-
function read_arguments(argv: ReadonlyArray<string>): ScoutArguments | undefined {
|
|
139
|
-
try {
|
|
140
|
-
return parse_arguments(argv)
|
|
141
|
-
} catch {
|
|
142
|
-
return undefined
|
|
143
|
-
}
|
|
135
|
+
return { keywords, size: parse_size(parsed.values.size) }
|
|
144
136
|
}
|
|
145
137
|
|
|
146
138
|
async function report(args: ScoutArguments): Promise<number> {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { ports, type PortEnvironment } from '#ports'
|
|
4
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
4
5
|
|
|
5
6
|
const SUCCESS_EXIT_CODE = 0
|
|
6
7
|
const USAGE_EXIT_CODE = 1
|
|
@@ -49,7 +50,7 @@ function run(argv: ReadonlyArray<string>, environment?: PortEnvironment): PortRe
|
|
|
49
50
|
return { text: String(RESOLVERS[name](environment)), exit_code: SUCCESS_EXIT_CODE }
|
|
50
51
|
} catch (error) {
|
|
51
52
|
return {
|
|
52
|
-
text:
|
|
53
|
+
text: error_text.message_of(error),
|
|
53
54
|
exit_code: FAILURE_EXIT_CODE,
|
|
54
55
|
}
|
|
55
56
|
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { adopt_toolkits } from '#scripts/adopt/adopt-toolkits'
|
|
2
|
+
import { propagate_run, type RunStep, type StepResult } from './propagate-run'
|
|
3
|
+
import { propagate_steps, type Release, type ReleasePlan } from './propagate-steps'
|
|
4
|
+
import { propagate_targets, type Manifest, type PropagateTarget } from './propagate-targets'
|
|
5
|
+
|
|
6
|
+
// What one `josh propagate` run carries, decided from the repository it runs in (joshuafolkken/kit#2879).
|
|
7
|
+
//
|
|
8
|
+
// The supplier is kit or any toolkit built on it — app-kit, game-kit — and a toolkit's propagation
|
|
9
|
+
// carries the base packages under it too, at the versions that toolkit itself has installed and was
|
|
10
|
+
// verified with rather than at the base's newest. Each consumer is delivered by the topmost toolkit
|
|
11
|
+
// it installs: a lower supplier skips it, because syncing only the base would write the overlay's
|
|
12
|
+
// paths back to the base's originals (`docs/sync.md` → base first, overlay after), and would hand the
|
|
13
|
+
// consumer a base the toolkit above it never verified. Discovery, ordering and the CLI name are
|
|
14
|
+
// `josh adopt`'s own reads (`scripts/adopt/adopt-toolkits.ts`), not a second copy of them.
|
|
15
|
+
|
|
16
|
+
const REFUSAL_PREFIX = 'Refusing to propagate:'
|
|
17
|
+
const SUPPLIER_REFUSAL = [
|
|
18
|
+
`${REFUSAL_PREFIX} this is not a ${adopt_toolkits.TOOLKIT_SCOPE} toolkit's own repository.`,
|
|
19
|
+
'Propagation carries a published release outward, so it runs from the toolkit that published it',
|
|
20
|
+
'— kit, or a toolkit built on it that ships a CLI. Run it from that checkout instead.',
|
|
21
|
+
].join('\n')
|
|
22
|
+
const UNRESOLVED_CAUSE =
|
|
23
|
+
'declared here but not installed with a runnable CLI, so the consumers would receive this toolkit without it. Run `pnpm install`.'
|
|
24
|
+
const MISPLACED_CAUSE =
|
|
25
|
+
'declared under `dependencies`, which leaves it out of the plan. Move it to `devDependencies`.'
|
|
26
|
+
const ARROW = ' → '
|
|
27
|
+
const LIST_SEPARATOR = ', '
|
|
28
|
+
|
|
29
|
+
interface Supplier {
|
|
30
|
+
package_name: string
|
|
31
|
+
bin_name: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface SupplierResolution {
|
|
35
|
+
supplier?: Supplier
|
|
36
|
+
refusal?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface BaseResolution {
|
|
40
|
+
bases?: Array<Release>
|
|
41
|
+
cause?: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface PlanResolution {
|
|
45
|
+
plan?: ReleasePlan
|
|
46
|
+
refusal?: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// A toolkit is a scoped package that ships a CLI — the same reading `josh adopt` applies to an
|
|
50
|
+
// installed one, applied here to the repository's own manifest.
|
|
51
|
+
function toolkit_name(manifest: Manifest): string | undefined {
|
|
52
|
+
const { name } = manifest
|
|
53
|
+
|
|
54
|
+
return name !== undefined && adopt_toolkits.is_toolkit_package(name) ? name : undefined
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function supplier_of(manifest: Manifest | undefined): Supplier | undefined {
|
|
58
|
+
const package_name = manifest === undefined ? undefined : toolkit_name(manifest)
|
|
59
|
+
if (manifest === undefined || package_name === undefined) return undefined
|
|
60
|
+
const bin_name = adopt_toolkits.bin_name_of(package_name, manifest)
|
|
61
|
+
|
|
62
|
+
return bin_name === undefined ? undefined : { package_name, bin_name }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function resolve_supplier(project_root: string): SupplierResolution {
|
|
66
|
+
const supplier = supplier_of(propagate_targets.read_manifest(project_root))
|
|
67
|
+
|
|
68
|
+
return supplier === undefined ? { refusal: SUPPLIER_REFUSAL } : { supplier }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// A base toolkit pinned to the version installed in the supplier's own checkout — the one the
|
|
72
|
+
// supplier's verification gate ran against.
|
|
73
|
+
function pin_installed(project_root: string, release: Release): Release | undefined {
|
|
74
|
+
const version = propagate_targets.installed_version(project_root, release.package_name)
|
|
75
|
+
|
|
76
|
+
return version === undefined ? undefined : { ...release, version }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Why a declared base toolkit would fall out of the plan, naming it — either way `josh adopt` refuses
|
|
80
|
+
// one for (joshuafolkken/kit#1540). A base left out is not merely left behind: every consumer that
|
|
81
|
+
// installs it would then read it as a toolkit above this one and be skipped.
|
|
82
|
+
function incomplete_cause(project_root: string): string | undefined {
|
|
83
|
+
const misplaced = adopt_toolkits.misplaced_toolkits(project_root)
|
|
84
|
+
if (misplaced.length > 0) return `${misplaced.join(LIST_SEPARATOR)} — ${MISPLACED_CAUSE}`
|
|
85
|
+
const unresolved = adopt_toolkits.unresolved_toolkits(project_root)
|
|
86
|
+
|
|
87
|
+
return unresolved.length === 0
|
|
88
|
+
? undefined
|
|
89
|
+
: `${unresolved.join(LIST_SEPARATOR)} — ${UNRESOLVED_CAUSE}`
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// The supplier's base toolkits, base first, or the names that cannot be carried.
|
|
93
|
+
function base_releases(project_root: string, supplier: Supplier): BaseResolution {
|
|
94
|
+
const cause = incomplete_cause(project_root)
|
|
95
|
+
if (cause !== undefined) return { cause }
|
|
96
|
+
|
|
97
|
+
const bases = adopt_toolkits
|
|
98
|
+
.discover_toolkits(project_root)
|
|
99
|
+
.filter((release) => release.package_name !== supplier.package_name)
|
|
100
|
+
.map((release) => pin_installed(project_root, release))
|
|
101
|
+
|
|
102
|
+
return bases.every((release) => release !== undefined) ? { bases } : { cause: UNRESOLVED_CAUSE }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// The releases one run carries: the base toolkits first, then the supplier at its own version, so the
|
|
106
|
+
// sync runs base first and the supplier's overlay is written last.
|
|
107
|
+
function build_plan(project_root: string, version: string): PlanResolution {
|
|
108
|
+
const { supplier, refusal } = resolve_supplier(project_root)
|
|
109
|
+
if (supplier === undefined) return { refusal: refusal ?? SUPPLIER_REFUSAL }
|
|
110
|
+
const { bases, cause } = base_releases(project_root, supplier)
|
|
111
|
+
if (bases === undefined) return { refusal: `${REFUSAL_PREFIX} ${cause ?? UNRESOLVED_CAUSE}` }
|
|
112
|
+
const releases = [...bases, { ...supplier, version }]
|
|
113
|
+
|
|
114
|
+
return { plan: { releases, origin: propagate_steps.PROPAGATE_ORIGIN } }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// The package the run publishes and waits for — by construction the plan's last release.
|
|
118
|
+
function supplier_name(plan: ReleasePlan): string {
|
|
119
|
+
return plan.releases.at(-1)?.package_name ?? ''
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// A declared scoped package counts as a toolkit unless it is demonstrably a library: installed, and
|
|
123
|
+
// shipping no CLI. One that is not installed yet cannot be told apart, and treating it as a toolkit
|
|
124
|
+
// errs toward leaving the consumer to the propagation above rather than reverting its overlay.
|
|
125
|
+
function may_be_toolkit(repository_path: string, package_name: string): boolean {
|
|
126
|
+
if (adopt_toolkits.read_bin_name(repository_path, package_name) !== undefined) return true
|
|
127
|
+
|
|
128
|
+
return adopt_toolkits.is_unreachable_toolkit(repository_path, package_name)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function carriers_of(target: PropagateTarget, plan: ReleasePlan): Array<string> {
|
|
132
|
+
const carried = new Set(plan.releases.map((release) => release.package_name))
|
|
133
|
+
|
|
134
|
+
return adopt_toolkits
|
|
135
|
+
.all_declared_toolkits(target.path)
|
|
136
|
+
.filter((name) => !carried.has(name) && may_be_toolkit(target.path, name))
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// A consumer that installs a toolkit this run does not carry is left to that toolkit's propagation.
|
|
140
|
+
// Only a candidate that would otherwise be processed or reported current is re-classified.
|
|
141
|
+
function mark_carried(target: PropagateTarget, plan: ReleasePlan): PropagateTarget {
|
|
142
|
+
if (target.state !== 'ready' && target.state !== 'up_to_date') return target
|
|
143
|
+
const carriers = carriers_of(target, plan)
|
|
144
|
+
|
|
145
|
+
return carriers.length === 0 ? target : { ...target, state: 'carried_above', carriers }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// What the dry run prints for the two steps that repeat per release: the versions each package is
|
|
149
|
+
// pinned to, and the sync order.
|
|
150
|
+
function plan_details(plan: ReleasePlan): Readonly<Record<string, string>> {
|
|
151
|
+
return {
|
|
152
|
+
[propagate_run.STEP_UPGRADE]: plan.releases
|
|
153
|
+
.map((release) => `${release.package_name}@${release.version}`)
|
|
154
|
+
.join(LIST_SEPARATOR),
|
|
155
|
+
[propagate_run.STEP_SYNC]: propagate_steps
|
|
156
|
+
.sync_commands(plan)
|
|
157
|
+
.map(({ command }) => command.join(' '))
|
|
158
|
+
.join(ARROW),
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// The dry-run runner: it reports what each step would do and touches nothing.
|
|
163
|
+
function create_step_describer(plan: ReleasePlan): RunStep {
|
|
164
|
+
const details = plan_details(plan)
|
|
165
|
+
|
|
166
|
+
return (target: PropagateTarget, step: string): StepResult => {
|
|
167
|
+
const detail = details[step]
|
|
168
|
+
const suffix = detail === undefined ? '' : `: ${detail}`
|
|
169
|
+
|
|
170
|
+
console.info(` ${target.repo}: would run ${step}${suffix}`)
|
|
171
|
+
|
|
172
|
+
return { step, is_ok: true }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const propagate_plan = {
|
|
177
|
+
resolve_supplier,
|
|
178
|
+
build_plan,
|
|
179
|
+
supplier_name,
|
|
180
|
+
mark_carried,
|
|
181
|
+
create_step_describer,
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export type { Supplier }
|
|
185
|
+
export { propagate_plan }
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { git_gh_exec } from '#scripts/git/git-gh-exec'
|
|
2
2
|
import { poll } from '#scripts/lib/poll'
|
|
3
3
|
import { with_page_size } from '#scripts/version/version-remote'
|
|
4
|
-
import { execaSync } from 'execa'
|
|
5
4
|
|
|
6
5
|
// Waiting for a specific version of this package to actually exist in the registry.
|
|
7
6
|
//
|
|
@@ -58,15 +57,15 @@ function finish(state: PublishWaitState, version: string, attempts: number): Pub
|
|
|
58
57
|
// reported apart, because one means wait longer and the other means something is broken.
|
|
59
58
|
function fetch_published_versions(versions_endpoint: string): Array<string> | undefined {
|
|
60
59
|
const endpoint = with_page_size(versions_endpoint, VERSIONS_PAGE_SIZE)
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
const stdout = git_gh_exec.read_gh_api_sync({
|
|
61
|
+
path: endpoint,
|
|
62
|
+
jq_filter: NAMES_JQ,
|
|
63
|
+
timeout_ms: GH_TIMEOUT_MS,
|
|
65
64
|
})
|
|
66
|
-
if (
|
|
65
|
+
if (stdout === undefined) return undefined
|
|
67
66
|
|
|
68
67
|
try {
|
|
69
|
-
const parsed: unknown = JSON.parse(
|
|
68
|
+
const parsed: unknown = JSON.parse(stdout)
|
|
70
69
|
|
|
71
70
|
return Array.isArray(parsed) ? parsed.filter((name) => typeof name === 'string') : undefined
|
|
72
71
|
} catch {
|