@mccune1224/pi-pstack 0.1.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.
Files changed (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +142 -0
  3. package/agents/comment-sicko.md +37 -0
  4. package/agents/poteto-agent.md +16 -0
  5. package/docs/guide/01-setup.md +49 -0
  6. package/docs/guide/02-poteto-mode.md +98 -0
  7. package/docs/guide/03-understand.md +61 -0
  8. package/docs/guide/04-design.md +81 -0
  9. package/docs/guide/05-build-and-clean.md +75 -0
  10. package/docs/guide/06-verify-and-ship.md +87 -0
  11. package/docs/guide/07-overnight.md +81 -0
  12. package/docs/guide/08-principles.md +69 -0
  13. package/docs/guide/09-make-it-yours.md +67 -0
  14. package/docs/guide/10-recipes-and-pitfalls.md +94 -0
  15. package/docs/guide/README.md +30 -0
  16. package/docs/guide/images/design.jpg +0 -0
  17. package/docs/guide/images/overnight.jpg +0 -0
  18. package/docs/guide/images/recipes.jpg +0 -0
  19. package/docs/guide/images/router.jpg +0 -0
  20. package/docs/guide/images/understanding.jpg +0 -0
  21. package/docs/guide/images/verification.jpg +0 -0
  22. package/extensions/index.ts +358 -0
  23. package/extensions/settings.test.ts +110 -0
  24. package/extensions/settings.ts +240 -0
  25. package/package.json +77 -0
  26. package/skills/architect/SKILL.md +83 -0
  27. package/skills/architect/references/design-red-flags.md +33 -0
  28. package/skills/architect/references/rationale-template.md +35 -0
  29. package/skills/architect/references/runner-prompt.md +20 -0
  30. package/skills/arena/SKILL.md +71 -0
  31. package/skills/automate-me/SKILL.md +109 -0
  32. package/skills/blast-radius/SKILL.md +50 -0
  33. package/skills/bro/SKILL.md +7 -0
  34. package/skills/control-cli/SKILL.md +109 -0
  35. package/skills/control-ui/SKILL.md +109 -0
  36. package/skills/create-skill/SKILL.md +69 -0
  37. package/skills/create-verification-skill/SKILL.md +44 -0
  38. package/skills/create-verification-skill/references/feature-map-example/README.md +47 -0
  39. package/skills/create-verification-skill/references/feature-map-example/create-note.md +39 -0
  40. package/skills/create-verification-skill/references/feature-map-example/search.md +45 -0
  41. package/skills/deslop/SKILL.md +22 -0
  42. package/skills/figure-it-out/SKILL.md +55 -0
  43. package/skills/how/SKILL.md +130 -0
  44. package/skills/how/references/critic-prompt.md +59 -0
  45. package/skills/how/references/critique-rubric.md +58 -0
  46. package/skills/how/references/explainer-prompt.md +55 -0
  47. package/skills/how/references/explorer-prompt.md +52 -0
  48. package/skills/interrogate/SKILL.md +112 -0
  49. package/skills/interrogate/references/code-quality-review.md +47 -0
  50. package/skills/interrogate/references/lead-judgment.md +58 -0
  51. package/skills/interrogate/references/reviewer-prompt.md +72 -0
  52. package/skills/interrogate/references/rubric.md +77 -0
  53. package/skills/maintain-verification-skill/SKILL.md +39 -0
  54. package/skills/no-comments/SKILL.md +24 -0
  55. package/skills/poteto-mode/SKILL.md +142 -0
  56. package/skills/poteto-mode/playbooks/authoring-a-skill.md +12 -0
  57. package/skills/poteto-mode/playbooks/autonomous-run.md +13 -0
  58. package/skills/poteto-mode/playbooks/autopilot-full.md +13 -0
  59. package/skills/poteto-mode/playbooks/autopilot-stack.md +16 -0
  60. package/skills/poteto-mode/playbooks/babysit.md +27 -0
  61. package/skills/poteto-mode/playbooks/bug-fix.md +17 -0
  62. package/skills/poteto-mode/playbooks/eval.md +27 -0
  63. package/skills/poteto-mode/playbooks/feature.md +21 -0
  64. package/skills/poteto-mode/playbooks/hillclimb.md +21 -0
  65. package/skills/poteto-mode/playbooks/investigation.md +14 -0
  66. package/skills/poteto-mode/playbooks/multi-phase-plan.md +155 -0
  67. package/skills/poteto-mode/playbooks/opening-a-pr.md +29 -0
  68. package/skills/poteto-mode/playbooks/orchestrate.md +113 -0
  69. package/skills/poteto-mode/playbooks/pause-safely.md +10 -0
  70. package/skills/poteto-mode/playbooks/perf-issue.md +24 -0
  71. package/skills/poteto-mode/playbooks/prototype.md +14 -0
  72. package/skills/poteto-mode/playbooks/refactoring.md +16 -0
  73. package/skills/poteto-mode/playbooks/runtime-forensics.md +11 -0
  74. package/skills/poteto-mode/playbooks/session-pickup.md +13 -0
  75. package/skills/poteto-mode/playbooks/shipping.md +20 -0
  76. package/skills/poteto-mode/playbooks/trace-forensics.md +14 -0
  77. package/skills/poteto-mode/playbooks/visual-parity.md +11 -0
  78. package/skills/poteto-mode/playbooks/worktree-cleanup.md +14 -0
  79. package/skills/poteto-mode/references/bugbot-triage.md +142 -0
  80. package/skills/poteto-mode/scripts/bootstrap.ts +62 -0
  81. package/skills/poteto-mode/scripts/bun.lock +67 -0
  82. package/skills/poteto-mode/scripts/check-plan.mjs +186 -0
  83. package/skills/poteto-mode/scripts/orch/orch.test.ts +634 -0
  84. package/skills/poteto-mode/scripts/orch/orch.ts +578 -0
  85. package/skills/poteto-mode/scripts/orch/store.ts +1607 -0
  86. package/skills/poteto-mode/scripts/package.json +16 -0
  87. package/skills/poteto-mode/scripts/watch-pr/cli.test.ts +224 -0
  88. package/skills/poteto-mode/scripts/watch-pr/cli.ts +223 -0
  89. package/skills/poteto-mode/scripts/watch-pr/fakes.test-helper.ts +118 -0
  90. package/skills/poteto-mode/scripts/watch-pr/github.test.ts +306 -0
  91. package/skills/poteto-mode/scripts/watch-pr/github.ts +699 -0
  92. package/skills/poteto-mode/scripts/watch-pr/policy.test.ts +420 -0
  93. package/skills/poteto-mode/scripts/watch-pr/policy.ts +832 -0
  94. package/skills/poteto-mode/scripts/watch-pr/render.ts +169 -0
  95. package/skills/poteto-mode/scripts/watch-pr/tsconfig.json +13 -0
  96. package/skills/poteto-mode/scripts/watch-pr/types.compile.ts +93 -0
  97. package/skills/poteto-mode/scripts/watch-pr/types.ts +401 -0
  98. package/skills/poteto-mode/scripts/watch-pr/watch-pr +6 -0
  99. package/skills/poteto-mode/scripts/worktree-audit.mjs +178 -0
  100. package/skills/principle-boundary-discipline/SKILL.md +34 -0
  101. package/skills/principle-build-the-lever/SKILL.md +23 -0
  102. package/skills/principle-encode-lessons-in-structure/SKILL.md +31 -0
  103. package/skills/principle-exhaust-the-design-space/SKILL.md +21 -0
  104. package/skills/principle-experience-first/SKILL.md +19 -0
  105. package/skills/principle-fix-root-causes/SKILL.md +23 -0
  106. package/skills/principle-foundational-thinking/SKILL.md +21 -0
  107. package/skills/principle-guard-the-context-window/SKILL.md +17 -0
  108. package/skills/principle-laziness-protocol/SKILL.md +18 -0
  109. package/skills/principle-make-operations-idempotent/SKILL.md +24 -0
  110. package/skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +22 -0
  111. package/skills/principle-minimize-reader-load/SKILL.md +23 -0
  112. package/skills/principle-model-the-domain/SKILL.md +26 -0
  113. package/skills/principle-never-block-on-the-human/SKILL.md +23 -0
  114. package/skills/principle-outcome-oriented-execution/SKILL.md +22 -0
  115. package/skills/principle-prove-it-works/SKILL.md +33 -0
  116. package/skills/principle-redesign-from-first-principles/SKILL.md +16 -0
  117. package/skills/principle-separate-before-serializing-shared-state/SKILL.md +16 -0
  118. package/skills/principle-sequence-verifiable-units/SKILL.md +22 -0
  119. package/skills/principle-subtract-before-you-add/SKILL.md +22 -0
  120. package/skills/principle-type-system-discipline/SKILL.md +31 -0
  121. package/skills/recall/SKILL.md +35 -0
  122. package/skills/reflect/SKILL.md +77 -0
  123. package/skills/reflect/references/divergent-reviewer.md +43 -0
  124. package/skills/reflect/references/judgment-reviewer.md +42 -0
  125. package/skills/reflect/references/synthesizer.md +56 -0
  126. package/skills/reflect/references/tooling-reviewer.md +57 -0
  127. package/skills/setup-pstack/SKILL.md +92 -0
  128. package/skills/show-me-your-work/SKILL.md +82 -0
  129. package/skills/show-me-your-work/references/decision-log-template.tsv +1 -0
  130. package/skills/show-me-your-work/scripts/log.mjs +30 -0
  131. package/skills/swarm/SKILL.md +46 -0
  132. package/skills/tdd/SKILL.md +44 -0
  133. package/skills/teach/SKILL.md +21 -0
  134. package/skills/technical-writing/SKILL.md +130 -0
  135. package/skills/typescript-best-practices/SKILL.md +28 -0
  136. package/skills/typescript-best-practices/references/patterns.md +292 -0
  137. package/skills/unslop/SKILL.md +80 -0
  138. package/skills/why/SKILL.md +229 -0
  139. package/skills/why/references/epistemics.md +144 -0
  140. package/skills/why/references/investigator-prompt.md +103 -0
  141. package/skills/why/references/source-playbook.md +17 -0
  142. package/skills/why/references/sources/code-archaeology.md +88 -0
  143. package/skills/why/references/sources/databricks.md +70 -0
  144. package/skills/why/references/sources/datadog.md +99 -0
  145. package/skills/why/references/sources/incident-postmortem.md +15 -0
  146. package/skills/why/references/sources/linear.md +48 -0
  147. package/skills/why/references/sources/notion.md +55 -0
  148. package/skills/why/references/sources/sentry.md +100 -0
  149. package/skills/why/references/sources/slack.md +54 -0
  150. package/skills/why/references/synthesizer-prompt.md +135 -0
@@ -0,0 +1,142 @@
1
+ # Bugbot triage
2
+
3
+ Use this reference when the Babysit playbook (`../playbooks/babysit.md`) handles Bugbot or review-automation comments. The goal is not to ignore Bugbot by default. The goal is to stop treating every comment as a required code change.
4
+
5
+ ## Decision rubric
6
+
7
+ Classify each Bugbot thread before acting:
8
+
9
+ - `fix`: The comment identifies a plausible correctness, security, privacy, data loss, auth, billing, migration, idempotency, race, or shipped-behavior issue. Fix it in the lowest owning PR, then reply with the commit SHA and resolve the thread.
10
+ - `dismiss`: The comment matches a documented low-risk noisy pattern, and the current code/context proves the concern does not need a code change. Reply with a short reason and resolve the thread.
11
+ - `ask`: The comment is novel, high-severity, security/privacy/data-related, or ambiguous. Ask the user instead of guessing.
12
+
13
+ When in doubt, ask. Skipping a noisy code-quality comment is cheap; skipping a real data or security bug is not.
14
+
15
+ ## Learned pattern format
16
+
17
+ Add future patterns in this shape:
18
+
19
+ ```markdown
20
+ ### <short pattern name>
21
+
22
+ - Confidence: candidate | recurring | strong
23
+ - Skip when: <conditions that must be true>
24
+ - Do not skip when: <risk boundaries>
25
+ - Example signal: <phrases or code context that identify the pattern>
26
+ - Source: <PR/comment URL or short historical note>
27
+ ```
28
+
29
+ Use `candidate` for one or two examples. Use `recurring` after multiple real dismissals. Use `strong` only when the pattern is narrow, repeatedly verified, and low-risk.
30
+
31
+ ## Recurring skip candidates
32
+
33
+ ### Intentional UI or design-system visual changes
34
+
35
+ - Confidence: candidate
36
+ - Skip when: The PR description, screenshots, design review, or nearby code makes the visual change explicit, and the Bugbot comment is only restating that a shared visual default changed.
37
+ - Do not skip when: The comment points to accessibility, focus visibility, keyboard navigation, color contrast, or a component API contract that the PR did not intentionally change.
38
+ - Example signal: Comments about focus outlines, button sizes, spacing, or shared component visual defaults where the owner replies "intentional" or "intended".
39
+
40
+ ### Upstack or stack-local usage Bugbot cannot see
41
+
42
+ - Confidence: candidate
43
+ - Skip when: Bugbot flags an export, component, helper, or file as unused, and `gt ls -s`, upper-stack diffs, or PR context shows it is used by a later PR in the stack.
44
+ - Do not skip when: The current PR is not part of a stack, the symbol is public API, or the supposed upstack use cannot be verified.
45
+ - Example signal: "Exported component is never used" with a human reply like "used upstack".
46
+
47
+ ### Temporary duplication during parallel implementation
48
+
49
+ - Confidence: candidate
50
+ - Skip when: The PR intentionally duplicates a small amount of code to keep a new path parallel to an old path that is being deleted, replaced, or proven out.
51
+ - Do not skip when: The duplicated code changes security, billing, data access, API behavior, or a long-lived shared abstraction would clearly reduce risk.
52
+ - Example signal: "Significant duplication" or "duplicated validation logic" where the owner explains the old path will be deleted or the duplicate logic is intentionally local.
53
+
54
+ ### Existing framework or component invariant covers the warning
55
+
56
+ - Confidence: candidate
57
+ - Skip when: The concern is already guaranteed by a shared component, framework contract, type invariant, or single source of truth visible in the current diff or nearby code.
58
+ - Do not skip when: The invariant is assumed but not enforced, depends on timing, or crosses async/state boundaries where values can diverge.
59
+ - Example signal: Comments about missing max-height on an inner popover when the shared popover enforces viewport bounds, or nullable values where the local checked value and passed value share the same source.
60
+
61
+ ### Owner-declared follow-up or deferred cleanup
62
+
63
+ - Confidence: candidate
64
+ - Skip when: The PR owner explicitly says the issue is a known follow-up, the behavior is not made worse by the current PR, and the comment is not about a high-risk area.
65
+ - Do not skip when: The agent is acting without owner input, the issue is medium/high severity product behavior, or deferring would merge a new regression.
66
+ - Example signal: "I'll worry about that later" or "we'll delete this eventually".
67
+
68
+ ### Self-withdrawn or explicit false-positive rule comments
69
+
70
+ - Confidence: recurring
71
+ - Skip when: The comment body or a later Bugbot reply explicitly says the finding is withdrawn, compliant, or a false positive, and the agent can verify the relevant rule locally.
72
+ - Do not skip when: The only evidence is a human saying "false positive" on a high-risk issue without explanation.
73
+ - Example signal: A file-naming rule comment whose body says the file is already compliant.
74
+
75
+ ## Ask by default
76
+
77
+ Do not auto-skip these categories, even if a previous PR dismissed something similar:
78
+
79
+ - Security, privacy, auth, billing, data retention, training-data, and permission-boundary findings.
80
+ - High-severity findings.
81
+ - Migration, schema, idempotency, concurrency, and cross-system behavior findings.
82
+ - Comments where the suggested fix is small and clearly reduces risk without changing product intent.
83
+
84
+ Historical data showed humans sometimes dismiss security/data-flow comments. Treat those as owner judgment calls, not team-wide skip rules.
85
+
86
+ ## Candidate learnings from recent babysits
87
+
88
+ Append new candidate learnings here during or after babysitting when they look team-useful but not yet mature. Prefer promoting recurring candidates into the section above once several PRs confirm the pattern.
89
+
90
+ ### Manual reimplementations of native browser behavior
91
+
92
+ - Confidence: candidate
93
+ - Skip when: Practically never. When a diff replaces native browser behavior with a manual equivalent (native sticky → JS-positioned clones, native scroll targeting → forwarded wheel/touch events, paint-order occlusion → masks/clip-path), Bugbot's logic-bug findings against that code have been consistently legitimate.
94
+ - Do not skip when: The finding concerns event-forwarding gaps (wheel deltaMode, touch pans, scroll-chaining at edges, tap slop), mask/clip hit-testing divergence, or observer-vs-React state timing races in such code. Default to fix.
95
+ - Example signal: "masks do not affect hit-testing", "overlay blocks wheel scroll", "ignores deltaMode", "runs in the IntersectionObserver callback before React applies state".
96
+ - Source: one sticky-occlusion PR: six Bugbot passes, roughly eighteen findings, every one fixed rather than dismissed.
97
+
98
+ ### Contract-test drift claims are cheaply verifiable — run the test first
99
+
100
+ - Confidence: candidate
101
+ - Skip when: Never skip the verification itself; it costs one command. When a PR
102
+ ships a contract test that pins protocol or documentation prose (regexes over
103
+ a SKILL.md, snapshot of doc wording), and Bugbot claims "the test no longer
104
+ matches the doc" (or vice versa), run that test on the PR tip before
105
+ classifying. A red run confirms the claim empirically; a green run is a
106
+ concrete disproof for the dismissal reply.
107
+ - Do not skip when: n/a — this is a verification shortcut, not a dismissal
108
+ pattern. Note that repeat-pass lean-dismiss heuristics would misfire here:
109
+ prose-pinning tests drift precisely BECAUSE earlier fix rounds edit the prose.
110
+ - Example signal: "Contract test omits the pre-fix wait" on a PR whose earlier
111
+ fix commits reworded the pinned passage; the test run on the tip failed on
112
+ exactly the cited assertion.
113
+ - Source: one prose-pinning PR with eight Bugbot passes; the claim was real on
114
+ pass 7 despite every earlier pass being fixed-and-resolved.
115
+
116
+ ### Stale security-review finding already fixed later in the same PR
117
+
118
+ - Confidence: candidate
119
+ - Skip when: An agentic security review (or similar) claims a missing authz/validation call, and the current PR tip clearly includes that exact gate (with tests), typically added in a later hardening commit after the review ran.
120
+ - Do not skip when: The cited helper is a no-op for the principal under discussion, the check runs after the side effect it guards, or coverage for the claimed principal is missing.
121
+ - Example signal: A HIGH "missing authorization check" finding while the exact guard is already called before the side effect on the tip.
122
+ - Source: one webhook-endpoint PR whose hardening commit postdated the review run.
123
+
124
+ ### Widening a deliberately narrow error condition would mask the real error
125
+
126
+ - Confidence: candidate
127
+ - Skip when: The finding asks to broaden a narrow error condition (a specific
128
+ `errno`, error code, or status class) into a catch-all, and that narrowness
129
+ encodes a real distinction. The canonical shape is a dependency fallback
130
+ gated on `ENOENT`: "binary is not installed" is a different situation from
131
+ "the command ran and failed". Retrying on any non-zero exit would re-run a
132
+ legitimate failure (not found, expired auth, network) against the fallback
133
+ and then report the fallback's error, hiding the true one.
134
+ - Do not skip when: The narrow condition misses a case in the SAME category
135
+ (another "binary unusable" errno such as `EACCES`, another transport-level
136
+ failure), the unhandled path loses data or leaves partial state, or the retry
137
+ is idempotent AND the original error is still surfaced.
138
+ - Example signal: "only retries when X fails with ENOENT … never tries the
139
+ fallback even when a working Y exists", pointing at code whose fallback
140
+ exists for a missing dependency rather than a failed operation.
141
+ - Source: one CLI-rename PR whose fallback existed for a missing binary rather
142
+ than a failed command.
@@ -0,0 +1,62 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+
5
+ const scriptsDirectory = import.meta.dir;
6
+ const nodeModulesDirectory = join(scriptsDirectory, "node_modules");
7
+ const commanderPackagePath = join(
8
+ nodeModulesDirectory,
9
+ "commander",
10
+ "package.json"
11
+ );
12
+ const installKeyPath = join(
13
+ nodeModulesDirectory,
14
+ ".poteto-mode-tools-install-key"
15
+ );
16
+
17
+ function currentInstallKey(): string {
18
+ return createHash("sha256")
19
+ .update(readFileSync(join(scriptsDirectory, "package.json")))
20
+ .update("\0")
21
+ .update(readFileSync(join(scriptsDirectory, "bun.lock")))
22
+ .digest("hex");
23
+ }
24
+
25
+ export function ensureDependenciesInstalled(): void {
26
+ const installKey = currentInstallKey();
27
+ if (
28
+ existsSync(commanderPackagePath) &&
29
+ existsSync(installKeyPath) &&
30
+ readFileSync(installKeyPath, "utf8").trim() === installKey
31
+ ) {
32
+ return;
33
+ }
34
+
35
+ const result = Bun.spawnSync(
36
+ [process.execPath, "install", "--frozen-lockfile"],
37
+ { cwd: scriptsDirectory }
38
+ );
39
+ if (result.exitCode !== 0) {
40
+ process.stdout.write(result.stdout);
41
+ process.stderr.write(result.stderr);
42
+ throw new Error(
43
+ `bun install --frozen-lockfile exited with status ${result.exitCode}`
44
+ );
45
+ }
46
+ if (!existsSync(commanderPackagePath)) {
47
+ throw new Error(
48
+ "bun install --frozen-lockfile completed without installing commander"
49
+ );
50
+ }
51
+
52
+ writeFileSync(installKeyPath, `${installKey}\n`);
53
+
54
+ const restarted = Bun.spawnSync([process.execPath, ...process.argv.slice(1)], {
55
+ cwd: process.cwd(),
56
+ env: process.env,
57
+ stdin: "inherit",
58
+ stdout: "inherit",
59
+ stderr: "inherit",
60
+ });
61
+ process.exit(restarted.exitCode ?? 1);
62
+ }
@@ -0,0 +1,67 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "configVersion": 1,
4
+ "workspaces": {
5
+ "": {
6
+ "name": "@cursor-skill/poteto-mode-tools",
7
+ "dependencies": {
8
+ "commander": "14.0.0",
9
+ },
10
+ "devDependencies": {
11
+ "bun-types": "latest",
12
+ "typescript": "latest",
13
+ },
14
+ },
15
+ },
16
+ "packages": {
17
+ "@types/node": ["@types/node@26.1.2", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg=="],
18
+
19
+ "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="],
20
+
21
+ "@typescript/typescript-darwin-arm64": ["@typescript/typescript-darwin-arm64@7.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="],
22
+
23
+ "@typescript/typescript-darwin-x64": ["@typescript/typescript-darwin-x64@7.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA=="],
24
+
25
+ "@typescript/typescript-freebsd-arm64": ["@typescript/typescript-freebsd-arm64@7.0.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ=="],
26
+
27
+ "@typescript/typescript-freebsd-x64": ["@typescript/typescript-freebsd-x64@7.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw=="],
28
+
29
+ "@typescript/typescript-linux-arm": ["@typescript/typescript-linux-arm@7.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ=="],
30
+
31
+ "@typescript/typescript-linux-arm64": ["@typescript/typescript-linux-arm64@7.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ=="],
32
+
33
+ "@typescript/typescript-linux-loong64": ["@typescript/typescript-linux-loong64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ=="],
34
+
35
+ "@typescript/typescript-linux-mips64el": ["@typescript/typescript-linux-mips64el@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA=="],
36
+
37
+ "@typescript/typescript-linux-ppc64": ["@typescript/typescript-linux-ppc64@7.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA=="],
38
+
39
+ "@typescript/typescript-linux-riscv64": ["@typescript/typescript-linux-riscv64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ=="],
40
+
41
+ "@typescript/typescript-linux-s390x": ["@typescript/typescript-linux-s390x@7.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw=="],
42
+
43
+ "@typescript/typescript-linux-x64": ["@typescript/typescript-linux-x64@7.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A=="],
44
+
45
+ "@typescript/typescript-netbsd-arm64": ["@typescript/typescript-netbsd-arm64@7.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA=="],
46
+
47
+ "@typescript/typescript-netbsd-x64": ["@typescript/typescript-netbsd-x64@7.0.2", "", { "os": "none", "cpu": "x64" }, "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA=="],
48
+
49
+ "@typescript/typescript-openbsd-arm64": ["@typescript/typescript-openbsd-arm64@7.0.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ=="],
50
+
51
+ "@typescript/typescript-openbsd-x64": ["@typescript/typescript-openbsd-x64@7.0.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg=="],
52
+
53
+ "@typescript/typescript-sunos-x64": ["@typescript/typescript-sunos-x64@7.0.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g=="],
54
+
55
+ "@typescript/typescript-win32-arm64": ["@typescript/typescript-win32-arm64@7.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ=="],
56
+
57
+ "@typescript/typescript-win32-x64": ["@typescript/typescript-win32-x64@7.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="],
58
+
59
+ "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
60
+
61
+ "commander": ["commander@14.0.0", "", {}, "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA=="],
62
+
63
+ "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="],
64
+
65
+ "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
66
+ }
67
+ }
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import process from "node:process";
4
+
5
+ const RULE =
6
+ "Tests alone are not sufficient verification. A PR is verified only when its unit, live, and perf boxes are all checked.";
7
+ const LANES = "Ten parallel lanes at the PR head";
8
+ const SUB_BLOCKS = [
9
+ "Depends on.",
10
+ "Files.",
11
+ "Build.",
12
+ "You see.",
13
+ "Verify, unit.",
14
+ "Verify, live.",
15
+ "Verify, perf.",
16
+ "Review gate.",
17
+ "Merge.",
18
+ ];
19
+ const PROGRAM_H3 = ["Arm the program", "Spawn owners", "PR mechanics", "Verdict and merge", "Boot recipe"];
20
+ const PROGRAM_MARKERS = ["/goal", "git show origin/main:", /30[- ]minute/, "status message"];
21
+ const HOW_TO_READ_MARKERS = [
22
+ "One box is one unit of work",
23
+ "names the evidence",
24
+ "Check a box only when its evidence exists",
25
+ "playbooks/",
26
+ RULE,
27
+ ];
28
+ const PERF_ITEMS = ["Metric.", "Probe.", "Baseline.", "Rule."];
29
+ const BOX = /^\s*- \[[ x]\] (.*)$/;
30
+
31
+ const file = process.argv[2];
32
+ if (!file) {
33
+ console.error("Usage: node check-plan.mjs <plan.md>");
34
+ process.exit(2);
35
+ }
36
+
37
+ const raw = fs.readFileSync(file, "utf8").split(/\r?\n/);
38
+ const problems = [];
39
+ const fail = (line, message) => problems.push(`${file}:${line}: ${message}`);
40
+
41
+ let start = 0;
42
+ if (raw[0] === "---") {
43
+ start = raw.indexOf("---", 1) + 1;
44
+ }
45
+
46
+ const lines = [];
47
+ let fence = false;
48
+ for (let i = start; i < raw.length; i++) {
49
+ const text = raw[i];
50
+ const n = i + 1;
51
+ if (/^```/.test(text)) fence = !fence;
52
+ lines.push({ n, text, code: fence });
53
+ if (fence) continue;
54
+ const prose = text
55
+ .replace(/`[^`]*`/g, "`")
56
+ .replace(/!\[[^\]]*\]\([^)]*\)/g, "")
57
+ .replace(/\]\([^)]*\)/g, "]");
58
+ if (/[\u2013\u2014]/.test(prose)) fail(n, "long dash");
59
+ if (/[\u2018\u2019\u201c\u201d]/.test(prose)) fail(n, "curly quote");
60
+ if (/: \S/.test(prose)) fail(n, "mid-sentence colon");
61
+ }
62
+
63
+ const h2 = (l) => (!l.code && l.text.startsWith("## ") ? l.text.slice(3).trim() : null);
64
+ const sections = [];
65
+ for (const l of lines) {
66
+ const title = h2(l);
67
+ if (title !== null) sections.push({ title, n: l.n, body: [] });
68
+ else if (sections.length) sections.at(-1).body.push(l);
69
+ }
70
+ const find = (title) => sections.find((s) => s.title === title);
71
+ const bodyText = (s) => s.body.map((l) => l.text).join("\n");
72
+ const boxes = (ls) => ls.filter((l) => !l.code && BOX.test(l.text)).map((l) => ({ n: l.n, text: l.text.match(BOX)[1] }));
73
+
74
+ const h1 = lines.findIndex((l) => !l.code && l.text.startsWith("# "));
75
+ if (h1 === -1) fail(1, "no H1 title");
76
+ const howToRead = find("How to read this");
77
+ if (!howToRead) fail(1, 'no "## How to read this" section');
78
+ if (h1 !== -1 && howToRead) {
79
+ const intro = lines.slice(h1 + 1).filter((l) => l.n < howToRead.n && l.text.trim() !== "");
80
+ if (intro.length >= 10) fail(lines[h1].n, `intro is ${intro.length} lines, under ten required`);
81
+ for (const marker of HOW_TO_READ_MARKERS) {
82
+ if (!bodyText(howToRead).includes(marker)) fail(howToRead.n, `How to read this lacks "${marker}"`);
83
+ }
84
+ }
85
+
86
+ const program = find("Program checklist");
87
+ if (!program) fail(1, 'no "## Program checklist" section');
88
+ else {
89
+ const h3s = program.body.filter((l) => !l.code && l.text.startsWith("### ")).map((l) => l.text.slice(4).trim());
90
+ let cursor = 0;
91
+ for (const name of PROGRAM_H3) {
92
+ const at = h3s.findIndex((t, i) => i >= cursor && t.startsWith(name));
93
+ if (at === -1) fail(program.n, `Program checklist lacks "### ${name}" in order`);
94
+ else cursor = at + 1;
95
+ }
96
+ for (const marker of PROGRAM_MARKERS) {
97
+ const ok = marker instanceof RegExp ? marker.test(bodyText(program)) : bodyText(program).includes(marker);
98
+ if (!ok) fail(program.n, `Program checklist lacks "${marker}"`);
99
+ }
100
+ }
101
+
102
+ const close = find("Close the program");
103
+ if (!close) fail(1, 'no "## Close the program" section');
104
+ const programIndex = sections.indexOf(program);
105
+ const closeIndex = sections.indexOf(close);
106
+ const prSections = programIndex === -1 || closeIndex === -1 ? [] : sections.slice(programIndex + 1, closeIndex);
107
+ if (prSections.length === 0) fail(1, "no PR sections between Program checklist and Close the program");
108
+
109
+ const report = [];
110
+ for (const pr of prSections) {
111
+ const heads = [];
112
+ for (const l of pr.body) {
113
+ if (l.code) continue;
114
+ const m = l.text.match(/^\*\*([^*]+)\*\*(.*)$/);
115
+ if (m && SUB_BLOCKS.includes(m[1])) heads.push({ name: m[1], n: l.n, rest: m[2].trim(), lines: [] });
116
+ else if (heads.length) heads.at(-1).lines.push(l);
117
+ }
118
+ const names = heads.map((h) => h.name);
119
+ if (names.join("|") !== SUB_BLOCKS.join("|")) {
120
+ fail(pr.n, `${pr.title}: sub-blocks are [${names.join(", ")}], expected [${SUB_BLOCKS.join(", ")}]`);
121
+ }
122
+ const block = (name) => heads.find((h) => h.name === name);
123
+ const counts = {};
124
+ for (const h of heads) counts[h.name] = boxes(h.lines).length;
125
+
126
+ const depends = block("Depends on.");
127
+ if (depends && depends.rest === "") fail(depends.n, `${pr.title}: Depends on names nothing`);
128
+ for (const name of ["Files.", "Build.", "You see.", "Verify, unit.", "Merge."]) {
129
+ const b = block(name);
130
+ if (b && boxes(b.lines).length === 0) fail(b.n, `${pr.title}: ${name} has no box`);
131
+ }
132
+ for (const name of ["Verify, unit.", "Verify, live.", "Verify, perf."]) {
133
+ const b = block(name);
134
+ if (b && !b.rest.startsWith(RULE)) fail(b.n, `${pr.title}: ${name} does not open with the rule`);
135
+ }
136
+
137
+ const live = block("Verify, live.");
138
+ if (live) {
139
+ if (!live.rest.includes(LANES)) fail(live.n, `${pr.title}: Verify, live lacks "${LANES}"`);
140
+ const lanes = boxes(live.lines).map((b) => ({ ...b, m: b.text.match(/^Lane (\d+)\. /) }));
141
+ const numbers = lanes.filter((b) => b.m).map((b) => Number(b.m[1])).sort((a, b) => a - b);
142
+ if (numbers.join(",") !== "1,2,3,4,5,6,7,8,9,10") fail(live.n, `${pr.title}: lanes are [${numbers.join(",")}], expected 1 to 10`);
143
+ for (const lane of lanes) {
144
+ if (!lane.m) fail(lane.n, `${pr.title}: live box is not a lane`);
145
+ else if (!/Save `[^`]+`/.test(lane.text)) fail(lane.n, `${pr.title}: lane ${lane.m[1]} names no screenshot`);
146
+ else if (!lane.text.includes("Pass when")) fail(lane.n, `${pr.title}: lane ${lane.m[1]} has no pass predicate`);
147
+ }
148
+ }
149
+
150
+ const perf = block("Verify, perf.");
151
+ if (perf) {
152
+ const items = boxes(perf.lines).map((b) => b.text.split(" ")[0]);
153
+ if (items.join("|") !== PERF_ITEMS.join("|")) fail(perf.n, `${pr.title}: perf boxes are [${items.join(", ")}], expected [${PERF_ITEMS.join(", ")}]`);
154
+ }
155
+
156
+ const gate = block("Review gate.");
157
+ if (gate) {
158
+ const gateBoxes = boxes(gate.lines);
159
+ if (gate.rest.startsWith("None.")) {
160
+ if (gateBoxes.length) fail(gate.n, `${pr.title}: Review gate says None but has boxes`);
161
+ } else {
162
+ const text = gate.lines.map((l) => l.text).join("\n");
163
+ if (gateBoxes.length === 0) fail(gate.n, `${pr.title}: Review gate has no box`);
164
+ for (const word of ["screenshot", "video", "operator"]) {
165
+ if (!text.includes(word)) fail(gate.n, `${pr.title}: Review gate lacks "${word}"`);
166
+ }
167
+ }
168
+ }
169
+
170
+ const total = boxes(pr.body).length;
171
+ const cells = SUB_BLOCKS.filter((s) => s !== "Depends on.").map((s) => `${s.replace(/[ ,.]+/g, "-").replace(/-$/, "").toLowerCase()}=${counts[s] ?? 0}`);
172
+ report.push(`${pr.title} boxes=${total} ${cells.join(" ")}`);
173
+ }
174
+
175
+ if (closeIndex !== -1) {
176
+ const tail = sections.slice(closeIndex + 1);
177
+ for (const s of tail) {
178
+ if (!s.title.startsWith("Appendix")) fail(s.n, `"## ${s.title}" after Close the program is not an appendix`);
179
+ }
180
+ if (!tail.some((s) => s.title.includes("Prototype evidence"))) fail(close.n, 'no "## Appendix ... Prototype evidence" section');
181
+ }
182
+
183
+ for (const line of report) console.log(line);
184
+ console.log(`${prSections.length} PR sections, ${problems.length} problems`);
185
+ for (const p of problems) console.error(p);
186
+ process.exit(problems.length ? 1 : 0);