@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,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
|
-
import { readFile } from 'node:fs/promises'
|
|
3
2
|
import { fileURLToPath } from 'node:url'
|
|
4
3
|
import { observation_ledger_home } from '#scripts/observations/observation-ledger-home'
|
|
5
4
|
import { review_finding_ledger, type CategoryCount } from './review-finding-ledger'
|
|
@@ -29,16 +28,10 @@ function format_report(counts: ReadonlyArray<CategoryCount>, zero_rounds: number
|
|
|
29
28
|
return `findings by category:\n${body}\n${zero_line(zero_rounds)}`
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return ''
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function run(ledger_path: string = observation_ledger_home.ledger_path()): Promise<number> {
|
|
41
|
-
const content = await read_ledger(ledger_path)
|
|
31
|
+
// Every issue's file of the ledger directory is counted (joshuafolkken/kit#2919): a recurring category
|
|
32
|
+
// is one whichever issue recorded each sighting.
|
|
33
|
+
async function run(root: string = observation_ledger_home.ledger_root()): Promise<number> {
|
|
34
|
+
const content = (await observation_ledger_home.read(root)) ?? ''
|
|
42
35
|
const counts = review_finding_ledger.category_counts(content)
|
|
43
36
|
|
|
44
37
|
console.info(format_report(counts, review_finding_ledger.zero_round_count(content)))
|
|
@@ -7,8 +7,8 @@ import { review_record, type RecordVerdict } from './review-record'
|
|
|
7
7
|
|
|
8
8
|
// `josh review:record --issue <N> [<category>:<severity>:<file> ...]` — the one write path for a
|
|
9
9
|
// `/code-review` round's findings (joshuafolkken/kit#2325). It appends a `- rf:` line per finding to
|
|
10
|
-
// the observation ledger, which
|
|
11
|
-
// change. **A call with no findings is a zero-finding round, and it still writes one line** — so the
|
|
10
|
+
// the issue's own file of the observation ledger, which the run's commit carries like any other
|
|
11
|
+
// ledger change. **A call with no findings is a zero-finding round, and it still writes one line** — so the
|
|
12
12
|
// round that found nothing is recorded rather than mistaken for a round nobody reviewed.
|
|
13
13
|
//
|
|
14
14
|
// `josh review:record --check --issue <N>` is the read half `pnpm josh followup` runs before it
|
|
@@ -121,14 +121,14 @@ function check_line(verdict: RecordVerdict): string {
|
|
|
121
121
|
return verdict.status === 'ok' ? RECORDED_LINE : NOT_REQUIRED_LINE
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
async function run_check(issue: string | undefined,
|
|
124
|
+
async function run_check(issue: string | undefined, root: string): Promise<number> {
|
|
125
125
|
if (issue === undefined || !ISSUE_PATTERN.test(issue)) {
|
|
126
126
|
console.error(CHECK_USAGE)
|
|
127
127
|
|
|
128
128
|
return FAILURE_EXIT_CODE
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
const verdict = await review_record.check(Number(issue),
|
|
131
|
+
const verdict = await review_record.check(Number(issue), root)
|
|
132
132
|
|
|
133
133
|
if (verdict.status === 'ok' || verdict.status === 'not-required') {
|
|
134
134
|
console.info(check_line(verdict))
|
|
@@ -141,7 +141,9 @@ async function run_check(issue: string | undefined, ledger_path: string): Promis
|
|
|
141
141
|
return FAILURE_EXIT_CODE
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
// The round lands in its own issue's file (joshuafolkken/kit#2919), so two lanes recording at once
|
|
145
|
+
// write two files, and their pull requests never conflict on the ledger.
|
|
146
|
+
async function run_record(parsed: Parsed, now: Date, root: string): Promise<number> {
|
|
145
147
|
const request = to_request(parsed)
|
|
146
148
|
|
|
147
149
|
if (request === undefined) {
|
|
@@ -151,6 +153,7 @@ async function run_record(parsed: Parsed, now: Date, ledger_path: string): Promi
|
|
|
151
153
|
}
|
|
152
154
|
|
|
153
155
|
const lines = build_lines(request, today(now))
|
|
156
|
+
const ledger_path = observation_ledger_home.issue_path(request.issue, root)
|
|
154
157
|
|
|
155
158
|
await observation_ledger_home.append(ledger_path, lines)
|
|
156
159
|
console.info(confirmation(lines.length, ledger_path))
|
|
@@ -158,13 +161,13 @@ async function run_record(parsed: Parsed, now: Date, ledger_path: string): Promi
|
|
|
158
161
|
return 0
|
|
159
162
|
}
|
|
160
163
|
|
|
161
|
-
// **The default is the
|
|
162
|
-
//
|
|
163
|
-
// `
|
|
164
|
+
// **The default is the work tree the command runs in, a lane's inside a lane** (joshuafolkken/kit#2919).
|
|
165
|
+
// The line rides that tree's own commit or `pnpm josh followup`'s pre-merge ledger commit, and
|
|
166
|
+
// `--check` reads the same tree, so the two halves can never disagree about where the round is.
|
|
164
167
|
async function run(
|
|
165
168
|
argv: ReadonlyArray<string>,
|
|
166
169
|
now: Date,
|
|
167
|
-
|
|
170
|
+
root: string = observation_ledger_home.ledger_root(),
|
|
168
171
|
): Promise<number> {
|
|
169
172
|
const parsed = parse_argv(argv)
|
|
170
173
|
|
|
@@ -174,9 +177,9 @@ async function run(
|
|
|
174
177
|
return FAILURE_EXIT_CODE
|
|
175
178
|
}
|
|
176
179
|
|
|
177
|
-
if (parsed.check) return await run_check(parsed.issue,
|
|
180
|
+
if (parsed.check) return await run_check(parsed.issue, root)
|
|
178
181
|
|
|
179
|
-
return await run_record(parsed, now,
|
|
182
|
+
return await run_record(parsed, now, root)
|
|
180
183
|
}
|
|
181
184
|
|
|
182
185
|
async function main(argv: ReadonlyArray<string>): Promise<void> {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises'
|
|
2
1
|
import { observation_ledger_home } from '#scripts/observations/observation-ledger-home'
|
|
3
2
|
import { review_finding_ledger } from './review-finding-ledger'
|
|
4
3
|
|
|
@@ -29,24 +28,15 @@ interface RecordVerdict {
|
|
|
29
28
|
issue?: number
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return await readFile(ledger_path, 'utf8')
|
|
37
|
-
} catch {
|
|
38
|
-
return undefined
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// The default is the primary checkout's ledger, the one `review:record` writes from a lane
|
|
43
|
-
// (joshuafolkken/kit#2419) — a cwd-relative default read the lane's copy, so the followup gate refused
|
|
44
|
-
// every round recorded from a lane (joshuafolkken/kit#2431).
|
|
31
|
+
// The default is the work tree the check runs in — the one `review:record` wrote to, a lane's in a
|
|
32
|
+
// lane (joshuafolkken/kit#2919). Every file of the directory is read, so the round is found whichever
|
|
33
|
+
// file holds it; a directory that is not there answers `undefined`, a ledger this checkout does not
|
|
34
|
+
// keep, which becomes `not-required`.
|
|
45
35
|
async function check(
|
|
46
36
|
issue: number,
|
|
47
|
-
|
|
37
|
+
root: string = observation_ledger_home.ledger_root(),
|
|
48
38
|
): Promise<RecordVerdict> {
|
|
49
|
-
const content = await
|
|
39
|
+
const content = await observation_ledger_home.read(root)
|
|
50
40
|
|
|
51
41
|
if (content === undefined) return { status: 'not-required' }
|
|
52
42
|
if (review_finding_ledger.has_issue_record(content, issue)) return { status: 'ok', issue }
|
|
@@ -59,10 +59,15 @@ const TEST_RELATED_PREFIX = 'josh-test-related-stamp-'
|
|
|
59
59
|
const GATE_PREFIX = 'josh-gate-stamp-'
|
|
60
60
|
const IN_FLIGHT_PREFIX = 'josh-gate-running-'
|
|
61
61
|
const ROUND_ONE_PREFIX = 'josh-review-round1-'
|
|
62
|
+
const BRIEFED_PREFIX = 'josh-review-briefed-'
|
|
62
63
|
|
|
63
64
|
const gate_stamp: FileMapStampAccess = file_map_stamp.create(GATE_PREFIX, PROJECT_ROOT)
|
|
64
65
|
const in_flight_stamp: FileMapStampAccess = file_map_stamp.create(IN_FLIGHT_PREFIX, PROJECT_ROOT)
|
|
65
66
|
const round_one_stamp: FileMapStampAccess = file_map_stamp.create(ROUND_ONE_PREFIX, PROJECT_ROOT)
|
|
67
|
+
// **The tree the latest round-1 brief handed out, retaken on every brief** (joshuafolkken/kit#2945).
|
|
68
|
+
// The round-1 snapshot is kept from the first brief of a run, so it is not the tree a later review
|
|
69
|
+
// read — a resume narrowed against it would skip a file put back to that older content.
|
|
70
|
+
const briefed_stamp: FileMapStampAccess = file_map_stamp.create(BRIEFED_PREFIX, PROJECT_ROOT)
|
|
66
71
|
const lint_related_stamp: FileMapStampAccess = file_map_stamp.create(
|
|
67
72
|
LINT_RELATED_PREFIX,
|
|
68
73
|
PROJECT_ROOT,
|
|
@@ -97,6 +102,8 @@ function clear_round_one(target?: string): void {
|
|
|
97
102
|
}
|
|
98
103
|
|
|
99
104
|
const review_stamps = {
|
|
105
|
+
BRIEFED_PREFIX,
|
|
106
|
+
briefed_stamp,
|
|
100
107
|
clear_round_one,
|
|
101
108
|
GATE_PREFIX,
|
|
102
109
|
gate_stamp,
|
|
@@ -47,7 +47,7 @@ const HUMAN_REVIEW = 'human-review'
|
|
|
47
47
|
// test. `LIVE` is the silent norm; `STOPPED` and `UNKNOWN` lead the difference lines.
|
|
48
48
|
const LIVE = 'live'
|
|
49
49
|
const STOPPED = 'stopped'
|
|
50
|
-
// The verdicts `epic --reconcile` prints; kept in step with the `
|
|
50
|
+
// The verdicts `epic --reconcile` prints; kept in step with the `epic_reconcile` constants in its
|
|
51
51
|
// test.
|
|
52
52
|
const RECONCILED = 'reconciled'
|
|
53
53
|
const NOTHING_TO_RECONCILE = 'nothing to reconcile'
|
|
@@ -302,7 +302,7 @@ const DECISION_ORACLES: ReadonlyArray<DecisionOracle> = [
|
|
|
302
302
|
decision: 'Whether cross-file or cross-repository code duplication exists',
|
|
303
303
|
args: '',
|
|
304
304
|
vocabulary: ['clean', 'clones:'],
|
|
305
|
-
single_source: 'prompts/collaboration-workflow/
|
|
305
|
+
single_source: 'prompts/collaboration-workflow/principles.md → クローン禁止',
|
|
306
306
|
},
|
|
307
307
|
{
|
|
308
308
|
name: 'epic:reconcile',
|
|
@@ -50,7 +50,7 @@ function is_impl_cut_segment(segment: string): boolean {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// Whether this command takes the implementation-phase cut, segment-wise so a spelling quoted inside a
|
|
53
|
-
// body is not read as the call
|
|
53
|
+
// body is not read as the call.
|
|
54
54
|
function takes_the_impl_cut(command: string): boolean {
|
|
55
55
|
return shell_segments.segments_of(command).some((segment) => is_impl_cut_segment(segment))
|
|
56
56
|
}
|
|
@@ -21,8 +21,7 @@ import { tail_commands } from './tail-commands'
|
|
|
21
21
|
// "fold first". **No new predicate**: the filing trigger is `bash_triggers.is_issue_filing`, and the
|
|
22
22
|
// prior-filing count is `filing_cap.prior_filing_count`, both reused verbatim.
|
|
23
23
|
|
|
24
|
-
// `pnpm josh issue:fold`, the one act the rule asks for.
|
|
25
|
-
// is read, so the canonical name matches both spellings.
|
|
24
|
+
// `pnpm josh issue:fold`, the one act the rule asks for.
|
|
26
25
|
const FOLD_NAMES: ReadonlySet<string> = new Set(['issue:fold'])
|
|
27
26
|
|
|
28
27
|
// Keeping the rule: a call that runs the fold. Segment-wise, so a spelling quoted inside a filing's
|
|
@@ -29,8 +29,7 @@ import { shell_segments } from './shell-segments'
|
|
|
29
29
|
|
|
30
30
|
// The parent backlogrun budget/ownership commands. A child never runs either: `run:carry` begins and
|
|
31
31
|
// advances the run budget the parent owns, and `run:merge` counts a returned child into it. Named in
|
|
32
|
-
// canonical form only — `shell_segments.is_josh_command` canonicalizes an alias
|
|
33
|
-
// the match, so `pnpm josh rmg` arrives as `run:merge`.
|
|
32
|
+
// canonical form only — `shell_segments.is_josh_command` canonicalizes an alias before the match.
|
|
34
33
|
const RUN_BUDGET_COMMANDS: ReadonlySet<string> = new Set(['run:merge', 'run:carry'])
|
|
35
34
|
|
|
36
35
|
// A segment that invokes one of the parent's budget commands. Segment-wise and alias-expanded for the
|
|
@@ -30,8 +30,7 @@ import { shell_segments } from './shell-segments'
|
|
|
30
30
|
// the stop, the reason is a checklist rather than an accusation, and a child that already parked simply
|
|
31
31
|
// reissues. One wasted round trip on the compliant path is the safe direction; a missed park is not.
|
|
32
32
|
|
|
33
|
-
// `josh notify`,
|
|
34
|
-
// same way `pre-gate-cut.ts` matches `gate` through `ga`.
|
|
33
|
+
// `josh notify`, named canonically — `shell_segments.is_josh_command` expands an alias before the match.
|
|
35
34
|
const NOTIFY_COMMANDS: ReadonlySet<string> = new Set(['notify'])
|
|
36
35
|
// The task type that marks a stop waiting on a person, taken quoted or bare so `--task-type
|
|
37
36
|
// confirmation` and `--task-type=confirmation` read alike. A `progress` or `pr` notification is not a
|
|
@@ -44,8 +44,8 @@ const HOLD_COMMANDS: ReadonlySet<string> = new Set(['run:hold'])
|
|
|
44
44
|
// predicate exists to keep it out of.
|
|
45
45
|
const HOLD_MODE_FLAG = /(?:^|\s)--release(?:[=\s]|$)/u
|
|
46
46
|
|
|
47
|
-
// **Alias-expanded rather than matched as text**, so `pnpm josh ga`
|
|
48
|
-
//
|
|
47
|
+
// **Alias-expanded rather than matched as text**, so `pnpm josh ga` is the same call as `pnpm josh
|
|
48
|
+
// gate`. Segment-wise for the reason every other row here is: one shell
|
|
49
49
|
// line carries several commands, and a name quoted inside a body is not the command being invoked.
|
|
50
50
|
//
|
|
51
51
|
// **One reading of that question rather than one per name set.** Four predicates here differ only in
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
2
1
|
import { json_value } from '#scripts/lib/json-value'
|
|
2
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
3
3
|
import type { GuardedCall } from '#scripts/time-runtime/time-batch-guard'
|
|
4
4
|
import { bash_triggers } from './bash-triggers'
|
|
5
5
|
import { shell_segments } from './shell-segments'
|
|
@@ -81,7 +81,7 @@ function string_field(input: Record<string, unknown>, key: string): string {
|
|
|
81
81
|
// measure against. Injected so the suite controls it without touching the real filesystem, the way
|
|
82
82
|
// `file-body.ts` injects its existence check. A new file reads as empty, so its whole body is added.
|
|
83
83
|
function read_existing(file_path: string): string {
|
|
84
|
-
return
|
|
84
|
+
return file_reader.read_file_or_empty(file_path)
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function texts_of(
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
2
|
+
import { mkdirSync, renameSync, statSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
5
|
+
|
|
6
|
+
// The `rule:value` reading at the `backlogrun` loop head, without the loop waiting on it
|
|
7
|
+
// (joshuafolkken/kit#2881). Measuring re-reads every transcript and takes minutes, and `backlog:offer`
|
|
8
|
+
// ran it synchronously on every iteration, so each dispatch waited on it. The loop head now prints the
|
|
9
|
+
// last reading from a cache and, when the last refresh is old enough, starts one in a detached process
|
|
10
|
+
// — `josh rule:value --refresh` — so the next iteration prints the newer reading.
|
|
11
|
+
//
|
|
12
|
+
// **It reports and never fails**, as `rule:value` itself does: a missing cache, an unwritable directory
|
|
13
|
+
// or a refresh that could not start prints at most nothing, and never changes the offer's answer.
|
|
14
|
+
|
|
15
|
+
const CACHE_DIRECTORY = ['node_modules', '.cache', 'josh']
|
|
16
|
+
const CACHE_FILE = 'rule-value.txt'
|
|
17
|
+
// Touched when a refresh starts, so the loop head starts at most one per interval however often it runs.
|
|
18
|
+
const STAMP_FILE = 'rule-value.stamp'
|
|
19
|
+
const MS_PER_MINUTE = 60_000
|
|
20
|
+
const REFRESH_MINUTES = 15
|
|
21
|
+
const REFRESH_MS = REFRESH_MINUTES * MS_PER_MINUTE
|
|
22
|
+
const REFRESH_ARGV = ['josh', 'rule:value', '--refresh']
|
|
23
|
+
const PNPM = 'pnpm'
|
|
24
|
+
const TEMPORARY_SUFFIX = '.tmp'
|
|
25
|
+
|
|
26
|
+
interface CachePorts {
|
|
27
|
+
now: () => number
|
|
28
|
+
start_refresh: (cwd: string) => void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function cache_path(cwd: string, file: string): string {
|
|
32
|
+
return path.join(cwd, ...CACHE_DIRECTORY, file)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function read_cached(cwd: string): string | undefined {
|
|
36
|
+
return file_reader.read_if_readable(cache_path(cwd, CACHE_FILE))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function is_refresh_due(cwd: string, now_ms: number): boolean {
|
|
40
|
+
try {
|
|
41
|
+
return now_ms - statSync(cache_path(cwd, STAMP_FILE)).mtimeMs >= REFRESH_MS
|
|
42
|
+
} catch {
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function write_cache_file(cwd: string, file: string, text: string): void {
|
|
48
|
+
const target = cache_path(cwd, file)
|
|
49
|
+
const temporary = `${target}${TEMPORARY_SUFFIX}`
|
|
50
|
+
|
|
51
|
+
mkdirSync(path.dirname(target), { recursive: true })
|
|
52
|
+
writeFileSync(temporary, text)
|
|
53
|
+
renameSync(temporary, target)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function ignore_spawn_error(): void {
|
|
57
|
+
/* a refresh that could not start leaves the last reading in place */
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Detached with no descriptor shared, so the loop head exits — and a backgrounded caller completes —
|
|
61
|
+
// without waiting on the measurement.
|
|
62
|
+
function start_refresh_default(cwd: string): void {
|
|
63
|
+
const child = spawn(PNPM, REFRESH_ARGV, { cwd, detached: true, stdio: 'ignore' })
|
|
64
|
+
|
|
65
|
+
child.on('error', ignore_spawn_error)
|
|
66
|
+
child.unref()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const DEFAULT_PORTS: CachePorts = { now: Date.now, start_refresh: start_refresh_default }
|
|
70
|
+
|
|
71
|
+
function refresh_if_due(cwd: string, ports: CachePorts): void {
|
|
72
|
+
if (!is_refresh_due(cwd, ports.now())) return
|
|
73
|
+
|
|
74
|
+
write_cache_file(cwd, STAMP_FILE, new Date(ports.now()).toISOString())
|
|
75
|
+
ports.start_refresh(cwd)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// The loop-head call: the last reading to stderr, then a refresh when one is due.
|
|
79
|
+
function emit(cwd: string = process.cwd(), ports: CachePorts = DEFAULT_PORTS): void {
|
|
80
|
+
const cached = read_cached(cwd)
|
|
81
|
+
|
|
82
|
+
if (cached !== undefined) process.stderr.write(cached)
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
refresh_if_due(cwd, ports)
|
|
86
|
+
} catch {
|
|
87
|
+
// A refresh that could not be recorded or started leaves the last reading in place.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// The detached refresh's body: the reading written whole, so a reader never sees half of one.
|
|
92
|
+
function store(text: string, cwd: string = process.cwd()): void {
|
|
93
|
+
write_cache_file(cwd, CACHE_FILE, `${text}\n`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const rule_value_cache = { CACHE_FILE, REFRESH_MS, STAMP_FILE, cache_path, emit, store }
|
|
97
|
+
|
|
98
|
+
export type { CachePorts }
|
|
99
|
+
export { rule_value_cache }
|
|
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
3
3
|
import { cost_transcript, type SessionFile } from '#scripts/cost-runtime/cost-transcript'
|
|
4
4
|
import { delivered_rules } from './delivered-rules'
|
|
5
5
|
import { rule_value, type RuleReading } from './rule-value'
|
|
6
|
+
import { rule_value_cache } from './rule-value-cache'
|
|
6
7
|
|
|
7
8
|
// `josh rule:value` — what each delivered rule earns on the channel that carries it, one row per rule
|
|
8
9
|
// (joshuafolkken/kit#2271). `rule_value.measure` was public but had no caller outside its own test, so
|
|
@@ -14,6 +15,8 @@ import { rule_value, type RuleReading } from './rule-value'
|
|
|
14
15
|
// no measurable transcript prints `NO_TARGETS` and exits zero rather than erroring, so a fresh
|
|
15
16
|
// checkout reads differently from a broken command.
|
|
16
17
|
|
|
18
|
+
const ARGV_OFFSET = 2
|
|
19
|
+
const REFRESH_FLAG = '--refresh'
|
|
17
20
|
const PERCENT_SIGN = '%'
|
|
18
21
|
const COLUMN_GAP = ' '
|
|
19
22
|
const NO_TARGETS = 'no measurement targets'
|
|
@@ -98,24 +101,22 @@ function render(runs: ReadonlyArray<ReadonlyArray<string>>): string {
|
|
|
98
101
|
.join('\n')
|
|
99
102
|
}
|
|
100
103
|
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
}
|
|
104
|
+
// `--refresh` is the detached measurement the `backlogrun` loop head starts: the reading goes to the
|
|
105
|
+
// cache `backlog:offer` prints from, never to stdout (`rule-value-cache.ts`, joshuafolkken/kit#2881).
|
|
106
|
+
function run_rule_value(argv: ReadonlyArray<string> = [], cwd: string = process.cwd()): number {
|
|
107
|
+
const reading = render(gather_runs(cwd))
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
process.stdout.write(`${
|
|
109
|
+
if (argv.includes(REFRESH_FLAG)) rule_value_cache.store(reading, cwd)
|
|
110
|
+
else process.stdout.write(`${reading}\n`)
|
|
109
111
|
|
|
110
112
|
return 0
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
114
|
-
process.exitCode = run_rule_value()
|
|
116
|
+
process.exitCode = run_rule_value(process.argv.slice(ARGV_OFFSET))
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
const rule_value_cli = {
|
|
118
|
-
emit,
|
|
119
120
|
gather_runs,
|
|
120
121
|
group_by_run,
|
|
121
122
|
render,
|
|
@@ -9,7 +9,7 @@ import { delivered_rules, type MeasuredRule } from './delivered-rules'
|
|
|
9
9
|
// (joshuafolkken/kit#1525).
|
|
10
10
|
//
|
|
11
11
|
// **It exists because the eviction order was decided by protection rather than by value.**
|
|
12
|
-
//
|
|
12
|
+
// `prompts/collaboration-workflow/residency.md` records that when a rule is trimmed to keep a
|
|
13
13
|
// byte count, the sentence that goes is the one no marker pinned (joshuafolkken/kit#951) — so the
|
|
14
14
|
// least-defended text leaves, never the least-useful. Nothing in the repository could tell the two
|
|
15
15
|
// apart: `scripts/josh/hook-decision.ts` keeps a once-per-run stamp that answers "has this fired
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
2
2
|
import { run_carry, type CarryChange, type CarryClaimRequest, type CarryOwner } from './run-carry'
|
|
3
3
|
import { run_issue_number } from './run-issue-number'
|
|
4
4
|
|
|
@@ -76,11 +76,7 @@ type Request =
|
|
|
76
76
|
const READ_REQUEST: Request = { kind: 'read' }
|
|
77
77
|
|
|
78
78
|
function read_arguments(argv: ReadonlyArray<string>): ParsedValues | undefined {
|
|
79
|
-
|
|
80
|
-
return parseArgs({ args: [...argv], options: OPTIONS, strict: true }).values
|
|
81
|
-
} catch {
|
|
82
|
-
return undefined
|
|
83
|
-
}
|
|
79
|
+
return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS, strict: true })?.values
|
|
84
80
|
}
|
|
85
81
|
|
|
86
82
|
function text_of(value: OptionValue): string | undefined {
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
type RunCarry,
|
|
11
11
|
} from './run-carry'
|
|
12
12
|
import { run_carry_args, type CountRequest, type Request } from './run-carry-args'
|
|
13
|
-
import { run_carry_flush } from './run-carry-flush'
|
|
14
13
|
import { run_carry_stash } from './run-carry-stash'
|
|
15
14
|
import { run_event_stream } from './run-event-stream'
|
|
16
15
|
import { run_event_stream_emit } from './run-event-stream-emit'
|
|
@@ -344,16 +343,15 @@ async function count(
|
|
|
344
343
|
// **A stop over a record that is still there pushes one ⏸️ confirmation before the record goes**
|
|
345
344
|
// (joshuafolkken/kit#2136). `plan` is read from the record `--end` is about to remove, so the second
|
|
346
345
|
// `--end` reads `none` and plans nothing — one stop never notifies twice. A clean `--end` passes no
|
|
347
|
-
// reason and stays silent, because a completed run has its own notification. The
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
//
|
|
351
|
-
//
|
|
346
|
+
// reason and stays silent, because a completed run has its own notification. The closed-issue stash
|
|
347
|
+
// report (joshuafolkken/kit#2505, `run-carry-stash.ts`) is once per invocation on the same read.
|
|
348
|
+
//
|
|
349
|
+
// **No ledger flush rides it any more** (joshuafolkken/kit#2919): each lane merges its own ledger
|
|
350
|
+
// lines with its own pull request, so there is nothing left in the primary checkout to flush.
|
|
352
351
|
async function report_end(read: CarryRead): Promise<void> {
|
|
353
352
|
if (read.kind === 'none') return
|
|
354
353
|
|
|
355
354
|
await run_carry_stash.report_orphans()
|
|
356
|
-
await run_carry_flush.flush_ledger()
|
|
357
355
|
}
|
|
358
356
|
|
|
359
357
|
async function finish(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
2
2
|
import { run_issue_number } from './run-issue-number'
|
|
3
3
|
|
|
4
4
|
// `josh run:cut` turns its `argv` into one of four requests. The contract mirrors `run-carry-args.ts`:
|
|
@@ -47,18 +47,12 @@ type Request =
|
|
|
47
47
|
| { kind: 'end' }
|
|
48
48
|
|
|
49
49
|
function read_arguments(argv: ReadonlyArray<string>): ParsedValues | undefined {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
return { values: parsed.values, positionals: parsed.positionals }
|
|
59
|
-
} catch {
|
|
60
|
-
return undefined
|
|
61
|
-
}
|
|
50
|
+
return cli_flags.parse_or_undefined({
|
|
51
|
+
args: [...argv],
|
|
52
|
+
options: OPTIONS,
|
|
53
|
+
strict: true,
|
|
54
|
+
allowPositionals: true,
|
|
55
|
+
})
|
|
62
56
|
}
|
|
63
57
|
|
|
64
58
|
// The issue is the sole positional, and it must be a real issue number: a cut relaunches
|
|
@@ -1,6 +1,7 @@
|
|
|
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
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
4
5
|
import {
|
|
5
6
|
run_ending,
|
|
6
7
|
UNREADABLE_VERDICT,
|
|
@@ -56,11 +57,7 @@ interface ParsedArguments {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
function read_arguments(argv: ReadonlyArray<string>): ParsedArguments | undefined {
|
|
59
|
-
|
|
60
|
-
return parseArgs({ args: [...argv], options: OPTIONS, allowPositionals: true })
|
|
61
|
-
} catch {
|
|
62
|
-
return undefined
|
|
63
|
-
}
|
|
60
|
+
return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS, allowPositionals: true })
|
|
64
61
|
}
|
|
65
62
|
|
|
66
63
|
function is_valid(parsed: ParsedArguments): boolean {
|
|
@@ -96,7 +93,7 @@ async function run(argv: ReadonlyArray<string>): Promise<number> {
|
|
|
96
93
|
try {
|
|
97
94
|
return report(await run_ending.check(request))
|
|
98
95
|
} catch (error) {
|
|
99
|
-
return report_unreadable(
|
|
96
|
+
return report_unreadable(error_text.message_of(error))
|
|
100
97
|
}
|
|
101
98
|
}
|
|
102
99
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
2
|
+
import { json_value } from '#scripts/lib/json-value'
|
|
2
3
|
import { z } from 'zod'
|
|
3
4
|
|
|
4
5
|
// The run's append-only, ordered event stream — the surface a reader who was away rebuilds the run's
|
|
@@ -143,11 +144,7 @@ function is_present(event: RunEvent | undefined): event is RunEvent {
|
|
|
143
144
|
// A malformed line reads as no event rather than throwing, so one truncated append — a write that lost a
|
|
144
145
|
// race — never makes the whole stream unreadable.
|
|
145
146
|
function parse_line(line: string): RunEvent | undefined {
|
|
146
|
-
|
|
147
|
-
return event_schema.safeParse(JSON.parse(line)).data
|
|
148
|
-
} catch {
|
|
149
|
-
return undefined
|
|
150
|
-
}
|
|
147
|
+
return event_schema.safeParse(json_value.parse_or_undefined(line)).data
|
|
151
148
|
}
|
|
152
149
|
|
|
153
150
|
// Every event on the stream, oldest first. An absent or unreadable file is an empty stream, the same
|
|
@@ -73,10 +73,27 @@ function is_owned_here(carry: RunCarry, ancestry: () => ReadonlySet<number>): bo
|
|
|
73
73
|
return carry.owner_pid !== undefined && ancestry().has(carry.owner_pid)
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
function
|
|
77
|
-
|
|
76
|
+
function driven_here(
|
|
77
|
+
read: CarryRead | undefined,
|
|
78
|
+
ancestry: () => ReadonlySet<number>,
|
|
79
|
+
): RunCarry | undefined {
|
|
80
|
+
if (read?.kind !== 'carried' || !is_driving(read)) return undefined
|
|
78
81
|
|
|
79
|
-
return is_owned_here(read.carry, ancestry)
|
|
82
|
+
return is_owned_here(read.carry, ancestry) ? read.carry : undefined
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The live, un-handed-off carry record this session drives as the `backlogrun` parent, or `undefined`
|
|
87
|
+
* for a lane child or any session that does not own one. Returned rather than tested so a caller that
|
|
88
|
+
* also needs the record's counters (the parent cut's cap, joshuafolkken/kit#2947) reads it once.
|
|
89
|
+
*/
|
|
90
|
+
async function driving_carry(
|
|
91
|
+
source: EnvironmentSource = process.env,
|
|
92
|
+
ancestry: () => ReadonlySet<number> = lane_reap.own_ancestry,
|
|
93
|
+
): Promise<RunCarry | undefined> {
|
|
94
|
+
if (lane_child_marker.marked_issue(source) !== undefined) return undefined
|
|
95
|
+
|
|
96
|
+
return driven_here(await read_carry_here(), ancestry)
|
|
80
97
|
}
|
|
81
98
|
|
|
82
99
|
/**
|
|
@@ -88,14 +105,13 @@ async function is_backlog_parent(
|
|
|
88
105
|
source: EnvironmentSource = process.env,
|
|
89
106
|
ancestry: () => ReadonlySet<number> = lane_reap.own_ancestry,
|
|
90
107
|
): Promise<boolean> {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return is_driven_here(await read_carry_here(), ancestry)
|
|
108
|
+
return (await driving_carry(source, ancestry)) !== undefined
|
|
94
109
|
}
|
|
95
110
|
|
|
96
111
|
const run_headless = {
|
|
97
112
|
HEADLESS_ENV_KEY,
|
|
98
113
|
current_carry,
|
|
114
|
+
driving_carry,
|
|
99
115
|
environment,
|
|
100
116
|
is_backlog_parent,
|
|
101
117
|
is_headless,
|
|
@@ -1,6 +1,7 @@
|
|
|
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
|
+
import { error_text } from '#scripts/lib/error-message'
|
|
4
5
|
import { run_issue_number } from './run-issue-number'
|
|
5
6
|
import {
|
|
6
7
|
MS_PER_MINUTE,
|
|
@@ -89,11 +90,7 @@ interface ParsedArguments {
|
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
function read_arguments(argv: ReadonlyArray<string>): ParsedArguments | undefined {
|
|
92
|
-
|
|
93
|
-
return parseArgs({ args: [...argv], options: OPTIONS, allowPositionals: true })
|
|
94
|
-
} catch {
|
|
95
|
-
return undefined
|
|
96
|
-
}
|
|
93
|
+
return cli_flags.parse_or_undefined({ args: [...argv], options: OPTIONS, allowPositionals: true })
|
|
97
94
|
}
|
|
98
95
|
|
|
99
96
|
function is_valid_target(parsed: ParsedArguments): boolean {
|
|
@@ -154,7 +151,7 @@ async function run(argv: ReadonlyArray<string>): Promise<number> {
|
|
|
154
151
|
try {
|
|
155
152
|
return report(await run_liveness.check(request))
|
|
156
153
|
} catch (error) {
|
|
157
|
-
return report_undetermined(
|
|
154
|
+
return report_undetermined(error_text.message_of(error))
|
|
158
155
|
}
|
|
159
156
|
}
|
|
160
157
|
|