potetos-for-everyone 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.
- package/.agents/plugins/marketplace.json +21 -0
- package/.claude-plugin/marketplace.json +32 -0
- package/.claude-plugin/plugin.json +36 -0
- package/.codex-plugin/plugin.json +13 -0
- package/.codex-plugin/prompts/architect.md +6 -0
- package/.codex-plugin/prompts/arena.md +6 -0
- package/.codex-plugin/prompts/automate-me.md +6 -0
- package/.codex-plugin/prompts/blast-radius.md +6 -0
- package/.codex-plugin/prompts/bro.md +6 -0
- package/.codex-plugin/prompts/create-verification-skill.md +6 -0
- package/.codex-plugin/prompts/figure-it-out.md +6 -0
- package/.codex-plugin/prompts/how.md +6 -0
- package/.codex-plugin/prompts/interrogate.md +6 -0
- package/.codex-plugin/prompts/maintain-verification-skill.md +6 -0
- package/.codex-plugin/prompts/make-bot-ui.md +6 -0
- package/.codex-plugin/prompts/no-comments.md +6 -0
- package/.codex-plugin/prompts/poteto-mode.md +6 -0
- package/.codex-plugin/prompts/recall.md +6 -0
- package/.codex-plugin/prompts/reflect.md +6 -0
- package/.codex-plugin/prompts/setup-pstack.md +6 -0
- package/.codex-plugin/prompts/show-me-your-work.md +6 -0
- package/.codex-plugin/prompts/swarm.md +6 -0
- package/.codex-plugin/prompts/tdd.md +6 -0
- package/.codex-plugin/prompts/teach.md +6 -0
- package/.codex-plugin/prompts/technical-writing.md +6 -0
- package/.codex-plugin/prompts/typescript-best-practices.md +6 -0
- package/.codex-plugin/prompts/unslop.md +6 -0
- package/.codex-plugin/prompts/why.md +6 -0
- package/AGENTS.md +11 -0
- package/LICENSE +22 -0
- package/NOTICE.md +17 -0
- package/PORTABILITY.md +29 -0
- package/README.md +483 -0
- package/UPSTREAM.json +9 -0
- package/UPSTREAM_INVENTORY.json +13 -0
- package/VERSION +1 -0
- package/adapters/README.md +9 -0
- package/adapters/registry.json +46 -0
- package/agents/comment-reviewer.md +3 -0
- package/agents/comment-sicko.md +31 -0
- package/agents/poteto-agent.md +14 -0
- package/assets/logo.png +0 -0
- package/automations/benny/FOR_AGENTS.md +5 -0
- package/automations/benny/README.md +10 -0
- package/automations/benny/skills/reproduce-and-fix-issues/SKILL.md +12 -0
- package/automations/benny/skills/triage-issue-reports/SKILL.md +11 -0
- package/automations/benny/templates/configuration.example.yaml +11 -0
- package/automations/benny/templates/reproduce-automation-prompt.md +1 -0
- package/automations/benny/templates/triage-automation-prompt.md +1 -0
- package/bin/potetos +547 -0
- package/docs/.nojekyll +0 -0
- package/docs/guide/01-setup.md +25 -0
- package/docs/guide/02-poteto-mode.md +11 -0
- package/docs/guide/03-understand.md +5 -0
- package/docs/guide/04-design.md +5 -0
- package/docs/guide/05-build-and-clean.md +5 -0
- package/docs/guide/06-verify-and-ship.md +5 -0
- package/docs/guide/07-overnight.md +5 -0
- package/docs/guide/08-principles.md +5 -0
- package/docs/guide/09-make-it-yours.md +5 -0
- package/docs/guide/10-recipes-and-pitfalls.md +8 -0
- package/docs/guide/README.md +16 -0
- package/docs/index.html +422 -0
- package/hooks/hooks.json +17 -0
- package/hooks/session-start-context.md +11 -0
- package/npm/core.mjs +318 -0
- package/npm/postinstall.mjs +22 -0
- package/npm/potetos.mjs +145 -0
- package/package.json +98 -0
- package/runtime/README.md +21 -0
- package/runtime/__init__.py +1 -0
- package/runtime/capabilities.json +36 -0
- package/runtime/config.example.json +17 -0
- package/runtime/runner.py +240 -0
- package/skills/architect/SKILL.md +14 -0
- package/skills/arena/SKILL.md +14 -0
- package/skills/automate-me/SKILL.md +11 -0
- package/skills/blast-radius/SKILL.md +11 -0
- package/skills/bro/SKILL.md +7 -0
- package/skills/create-verification-skill/SKILL.md +11 -0
- package/skills/figure-it-out/SKILL.md +13 -0
- package/skills/how/SKILL.md +14 -0
- package/skills/interrogate/SKILL.md +14 -0
- package/skills/maintain-verification-skill/SKILL.md +11 -0
- package/skills/make-bot-ui/SKILL.md +11 -0
- package/skills/no-comments/SKILL.md +11 -0
- package/skills/poteto-mode/SKILL.md +93 -0
- package/skills/poteto-mode/playbooks/authoring-a-skill.md +7 -0
- package/skills/poteto-mode/playbooks/autonomous-run.md +7 -0
- package/skills/poteto-mode/playbooks/autopilot-full.md +7 -0
- package/skills/poteto-mode/playbooks/autopilot-stack.md +7 -0
- package/skills/poteto-mode/playbooks/babysit.md +7 -0
- package/skills/poteto-mode/playbooks/bug-fix.md +8 -0
- package/skills/poteto-mode/playbooks/eval.md +7 -0
- package/skills/poteto-mode/playbooks/feature.md +8 -0
- package/skills/poteto-mode/playbooks/hillclimb.md +8 -0
- package/skills/poteto-mode/playbooks/investigation.md +7 -0
- package/skills/poteto-mode/playbooks/multi-phase-plan.md +7 -0
- package/skills/poteto-mode/playbooks/opening-a-pr.md +7 -0
- package/skills/poteto-mode/playbooks/orchestrate.md +8 -0
- package/skills/poteto-mode/playbooks/pause-safely.md +7 -0
- package/skills/poteto-mode/playbooks/perf-issue.md +8 -0
- package/skills/poteto-mode/playbooks/prototype.md +7 -0
- package/skills/poteto-mode/playbooks/refactoring.md +7 -0
- package/skills/poteto-mode/playbooks/runtime-forensics.md +7 -0
- package/skills/poteto-mode/playbooks/session-pickup.md +7 -0
- package/skills/poteto-mode/playbooks/shipping.md +7 -0
- package/skills/poteto-mode/playbooks/trace-forensics.md +7 -0
- package/skills/poteto-mode/playbooks/visual-parity.md +7 -0
- package/skills/poteto-mode/playbooks/worktree-cleanup.md +7 -0
- package/skills/principle-attack-the-premise/SKILL.md +20 -0
- package/skills/principle-boundary-discipline/SKILL.md +20 -0
- package/skills/principle-build-the-lever/SKILL.md +20 -0
- package/skills/principle-encode-lessons-in-structure/SKILL.md +20 -0
- package/skills/principle-exhaust-the-design-space/SKILL.md +20 -0
- package/skills/principle-experience-first/SKILL.md +20 -0
- package/skills/principle-fix-root-causes/SKILL.md +20 -0
- package/skills/principle-foundational-thinking/SKILL.md +20 -0
- package/skills/principle-guard-the-context-window/SKILL.md +20 -0
- package/skills/principle-laziness-protocol/SKILL.md +20 -0
- package/skills/principle-make-operations-idempotent/SKILL.md +20 -0
- package/skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +20 -0
- package/skills/principle-minimize-reader-load/SKILL.md +20 -0
- package/skills/principle-model-the-domain/SKILL.md +20 -0
- package/skills/principle-never-block-on-the-human/SKILL.md +20 -0
- package/skills/principle-outcome-oriented-execution/SKILL.md +20 -0
- package/skills/principle-prove-it-works/SKILL.md +20 -0
- package/skills/principle-redesign-from-first-principles/SKILL.md +20 -0
- package/skills/principle-separate-before-serializing-shared-state/SKILL.md +20 -0
- package/skills/principle-sequence-verifiable-units/SKILL.md +20 -0
- package/skills/principle-subtract-before-you-add/SKILL.md +20 -0
- package/skills/principle-test-behavior-not-implementation/SKILL.md +20 -0
- package/skills/principle-type-system-discipline/SKILL.md +20 -0
- package/skills/recall/SKILL.md +13 -0
- package/skills/reflect/SKILL.md +11 -0
- package/skills/setup-pstack/SKILL.md +12 -0
- package/skills/show-me-your-work/SKILL.md +17 -0
- package/skills/swarm/SKILL.md +12 -0
- package/skills/tdd/SKILL.md +12 -0
- package/skills/teach/SKILL.md +11 -0
- package/skills/technical-writing/SKILL.md +12 -0
- package/skills/typescript-best-practices/SKILL.md +14 -0
- package/skills/unslop/SKILL.md +11 -0
- package/skills/why/SKILL.md +14 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Benny, portable pack
|
|
2
|
+
|
|
3
|
+
Benny is an optional issue-report automation pattern adapted from the Benny pack in Lauren Tan's pstack. Upstream wires it to Cursor automations and Slack. This version keeps the two-stage workflow but does not assume either product:
|
|
4
|
+
|
|
5
|
+
1. `triage-issue-reports` classifies and validates incoming reports.
|
|
6
|
+
2. `reproduce-and-fix-issues` reproduces confirmed defects and may prepare a small draft fix.
|
|
7
|
+
|
|
8
|
+
A host may trigger these from Slack, GitHub Issues, Linear, email, a webhook queue, or a human prompt. Keep source credentials and routing outside this pack. Always reply in the originating thread/ticket when the host exposes one.
|
|
9
|
+
|
|
10
|
+
This pack is dormant by default and is not installed with the main 47 skills.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reproduce-and-fix-issues
|
|
3
|
+
description: Reproduce a confirmed issue on the closest real surface, find root cause, and optionally prepare a minimal draft fix with evidence. Use after issue triage has enough information to attempt reproduction.
|
|
4
|
+
---
|
|
5
|
+
# Reproduce and fix issues
|
|
6
|
+
|
|
7
|
+
1. Work in an isolated branch/worktree/sandbox when the host can provide one.
|
|
8
|
+
2. Reproduce the exact reported behavior and save the smallest durable evidence.
|
|
9
|
+
3. If reproduction fails, vary only evidence-backed environment assumptions; return the blocker instead of guessing.
|
|
10
|
+
4. Trace root cause. If a cheap behavioral regression test exists, see it fail for the same reason.
|
|
11
|
+
5. Make the smallest fix that explains the reproduction, then rerun the reproduction and targeted checks.
|
|
12
|
+
6. Prepare a draft change/PR only when authorized. Post status back to the source thread/ticket with proof and remaining uncertainty.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: triage-issue-reports
|
|
3
|
+
description: Triage incoming software issue reports using available repository and runtime evidence, without editing code. Use for automated or manual first-pass issue validation and routing.
|
|
4
|
+
---
|
|
5
|
+
# Triage issue reports
|
|
6
|
+
|
|
7
|
+
1. Normalize the report into product area, environment, steps, expected behavior, observed behavior, frequency, and supplied evidence.
|
|
8
|
+
2. Search for duplicates and known limitations when issue/search capabilities exist.
|
|
9
|
+
3. Inspect the relevant code/feature map enough to judge plausibility without making edits.
|
|
10
|
+
4. Classify as confirmed-enough-to-reproduce, needs-reporter-information, duplicate, expected behavior, or currently untriageable.
|
|
11
|
+
5. Reply in the source thread/ticket when available with concise rationale and the next action. Do not invent a reproduction.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Host-owned example. Do not put secrets in committed configuration.
|
|
2
|
+
source:
|
|
3
|
+
kind: issue-tracker-or-chat
|
|
4
|
+
project: your-project
|
|
5
|
+
routing:
|
|
6
|
+
triage: triage-issue-reports
|
|
7
|
+
confirmed_bug: reproduce-and-fix-issues
|
|
8
|
+
safety:
|
|
9
|
+
triage_read_only: true
|
|
10
|
+
fixes_are_draft_by_default: true
|
|
11
|
+
never_merge_automatically: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Use the `reproduce-and-fix-issues` skill on this confirmed report. Reproduce before editing, isolate workspace changes when possible, prepare only a minimal draft fix, and return proof to the originating thread/ticket.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Use the `triage-issue-reports` skill on the incoming report. Preserve its source thread/ticket identity, do not edit code, and return only evidence-backed classification plus the next action.
|
package/bin/potetos
ADDED
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import shutil
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from types import SimpleNamespace
|
|
12
|
+
|
|
13
|
+
REPO = Path(__file__).resolve().parents[1]
|
|
14
|
+
sys.path.insert(0, str(REPO))
|
|
15
|
+
from runtime.runner import load_config, run_panel, run_worker
|
|
16
|
+
|
|
17
|
+
REGISTRY = json.loads((REPO / "adapters" / "registry.json").read_text(encoding="utf-8"))
|
|
18
|
+
BEGIN = "<!-- potetos-for-everyone:begin -->"
|
|
19
|
+
END = "<!-- potetos-for-everyone:end -->"
|
|
20
|
+
GITIGNORE_BEGIN = "# potetos-for-everyone:begin"
|
|
21
|
+
GITIGNORE_END = "# potetos-for-everyone:end"
|
|
22
|
+
MANIFEST = ".potetos/install.json"
|
|
23
|
+
|
|
24
|
+
BLOCK = f"""{BEGIN}
|
|
25
|
+
## potetos-for-everyone
|
|
26
|
+
|
|
27
|
+
For non-trivial engineering work, use the Agent Skill at `.agents/skills/poteto-mode/SKILL.md`.
|
|
28
|
+
It routes the task to a playbook, loads supporting skills progressively, prefers simple changes,
|
|
29
|
+
and requires evidence against the real artifact. Canonical skills are adapted from Lauren Tan's pstack.
|
|
30
|
+
If native skill discovery is unavailable, read that SKILL.md and its selected playbook manually.
|
|
31
|
+
{END}"""
|
|
32
|
+
|
|
33
|
+
GITIGNORE_BLOCK = f"""{GITIGNORE_BEGIN}
|
|
34
|
+
.potetos/
|
|
35
|
+
{GITIGNORE_END}"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def die(msg: str, code: int = 2):
|
|
39
|
+
print(f"potetos: {msg}", file=sys.stderr)
|
|
40
|
+
raise SystemExit(code)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def merge_block(path: Path):
|
|
44
|
+
old = path.read_text(encoding="utf-8") if path.exists() else ""
|
|
45
|
+
if BEGIN in old and END in old:
|
|
46
|
+
pre, rest = old.split(BEGIN, 1)
|
|
47
|
+
_, post = rest.split(END, 1)
|
|
48
|
+
new = pre.rstrip() + ("\n\n" if pre.strip() else "") + BLOCK + post
|
|
49
|
+
else:
|
|
50
|
+
new = old.rstrip() + ("\n\n" if old.strip() else "") + BLOCK + "\n"
|
|
51
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
52
|
+
path.write_text(new, encoding="utf-8")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def remove_block(path: Path):
|
|
56
|
+
if not path.exists():
|
|
57
|
+
return
|
|
58
|
+
text = path.read_text(encoding="utf-8")
|
|
59
|
+
if BEGIN not in text or END not in text:
|
|
60
|
+
return
|
|
61
|
+
pre, rest = text.split(BEGIN, 1)
|
|
62
|
+
_, post = rest.split(END, 1)
|
|
63
|
+
text = (pre.rstrip() + ("\n\n" if pre.strip() and post.strip() else "") + post.lstrip()).rstrip() + "\n"
|
|
64
|
+
if text.strip():
|
|
65
|
+
path.write_text(text, encoding="utf-8")
|
|
66
|
+
else:
|
|
67
|
+
path.unlink()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def merge_gitignore(path: Path):
|
|
71
|
+
old = path.read_text(encoding="utf-8") if path.exists() else ""
|
|
72
|
+
if GITIGNORE_BEGIN in old and GITIGNORE_END in old:
|
|
73
|
+
pre, rest = old.split(GITIGNORE_BEGIN, 1)
|
|
74
|
+
_, post = rest.split(GITIGNORE_END, 1)
|
|
75
|
+
new = pre.rstrip() + ("\n\n" if pre.strip() else "") + GITIGNORE_BLOCK + post
|
|
76
|
+
else:
|
|
77
|
+
new = old.rstrip() + ("\n\n" if old.strip() else "") + GITIGNORE_BLOCK + "\n"
|
|
78
|
+
path.write_text(new, encoding="utf-8")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def remove_gitignore(path: Path):
|
|
82
|
+
if not path.exists():
|
|
83
|
+
return
|
|
84
|
+
text = path.read_text(encoding="utf-8")
|
|
85
|
+
if GITIGNORE_BEGIN not in text or GITIGNORE_END not in text:
|
|
86
|
+
return
|
|
87
|
+
pre, rest = text.split(GITIGNORE_BEGIN, 1)
|
|
88
|
+
_, post = rest.split(GITIGNORE_END, 1)
|
|
89
|
+
text = (pre.rstrip() + ("\n\n" if pre.strip() and post.strip() else "") + post.lstrip()).rstrip() + "\n"
|
|
90
|
+
if text.strip():
|
|
91
|
+
path.write_text(text, encoding="utf-8")
|
|
92
|
+
else:
|
|
93
|
+
path.unlink()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _prune_empty_parents(path: Path, stop: Path):
|
|
97
|
+
stop = stop.resolve()
|
|
98
|
+
current = path
|
|
99
|
+
while current != stop and stop in current.resolve().parents:
|
|
100
|
+
try:
|
|
101
|
+
current.rmdir()
|
|
102
|
+
except OSError:
|
|
103
|
+
break
|
|
104
|
+
current = current.parent
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
_TEXT_HASH_SUFFIXES = {".md", ".mdc", ".txt", ".json", ".yml", ".yaml", ".js", ".mjs", ".py", ".sh", ".ps1"}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _hash_bytes(path: Path) -> bytes:
|
|
111
|
+
data = path.read_bytes()
|
|
112
|
+
if path.suffix.lower() in _TEXT_HASH_SUFFIXES:
|
|
113
|
+
data = data.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
|
|
114
|
+
return data
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _hash_path(path: Path) -> str:
|
|
118
|
+
h = hashlib.sha512()
|
|
119
|
+
root = path.resolve() if path.is_symlink() else path
|
|
120
|
+
if root.is_file():
|
|
121
|
+
h.update(_hash_bytes(root))
|
|
122
|
+
return h.hexdigest()
|
|
123
|
+
for item in sorted(p for p in root.rglob("*") if p.is_file()):
|
|
124
|
+
h.update(item.relative_to(root).as_posix().encode("utf-8"))
|
|
125
|
+
h.update(b"\0")
|
|
126
|
+
h.update(_hash_bytes(item))
|
|
127
|
+
h.update(b"\0")
|
|
128
|
+
return h.hexdigest()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _selected_agents(name: str) -> list[str]:
|
|
132
|
+
if name == "all":
|
|
133
|
+
# universal/generic only add AGENTS.md, so keep one of them and every
|
|
134
|
+
# specialized host shim. This avoids redundant manifest entries.
|
|
135
|
+
return ["universal"] + [n for n in REGISTRY if n not in {"generic", "universal"}]
|
|
136
|
+
if name not in REGISTRY:
|
|
137
|
+
die(f"unknown agent {name!r}; choose one of: all, {', '.join(sorted(REGISTRY))}")
|
|
138
|
+
return [name]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _skill_destination(target: Path, agents: list[str]) -> Path:
|
|
142
|
+
skill_dirs = {REGISTRY[name]["skill_dir"] for name in agents}
|
|
143
|
+
if len(skill_dirs) != 1:
|
|
144
|
+
die("adapter registry uses multiple skill directories; one canonical install is required")
|
|
145
|
+
return target / next(iter(skill_dirs))
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _instruction_files(agents: list[str]) -> list[str]:
|
|
149
|
+
return list(dict.fromkeys(["AGENTS.md"] + [REGISTRY[name]["instruction_file"] for name in agents]))
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _install_impl(*, target: Path, agent_selection: str, mode: str, force: bool, manage_gitignore: bool):
|
|
153
|
+
if not target.exists():
|
|
154
|
+
die(f"target does not exist: {target}")
|
|
155
|
+
agents = _selected_agents(agent_selection)
|
|
156
|
+
skills_dst = _skill_destination(target, agents)
|
|
157
|
+
skills_dst.mkdir(parents=True, exist_ok=True)
|
|
158
|
+
if mode == "link":
|
|
159
|
+
probe = skills_dst / ".potetos-link-probe"
|
|
160
|
+
try:
|
|
161
|
+
probe.symlink_to(REPO / "skills", target_is_directory=True)
|
|
162
|
+
probe.unlink()
|
|
163
|
+
except OSError:
|
|
164
|
+
probe.unlink(missing_ok=True)
|
|
165
|
+
print("potetos: symlinks unavailable; falling back to copies", file=sys.stderr)
|
|
166
|
+
mode = "copy"
|
|
167
|
+
installed: list[str] = []
|
|
168
|
+
sources = [src for src in sorted((REPO / "skills").iterdir()) if src.is_dir() and (src / "SKILL.md").exists()]
|
|
169
|
+
|
|
170
|
+
identical_existing: set[Path] = set()
|
|
171
|
+
if not force:
|
|
172
|
+
collisions = []
|
|
173
|
+
for src in sources:
|
|
174
|
+
dst = skills_dst / src.name
|
|
175
|
+
if not (dst.exists() or dst.is_symlink()):
|
|
176
|
+
continue
|
|
177
|
+
if dst.is_symlink() and dst.resolve() == src.resolve() and mode == "link":
|
|
178
|
+
identical_existing.add(dst)
|
|
179
|
+
continue
|
|
180
|
+
if mode == "copy" and dst.is_dir() and not dst.is_symlink() and _hash_path(dst) == _hash_path(src):
|
|
181
|
+
identical_existing.add(dst)
|
|
182
|
+
continue
|
|
183
|
+
collisions.append(dst)
|
|
184
|
+
if collisions:
|
|
185
|
+
sample = ", ".join(str(p) for p in collisions[:5])
|
|
186
|
+
more = f" (+{len(collisions) - 5} more)" if len(collisions) > 5 else ""
|
|
187
|
+
die(f"skill destinations already exist with different content: {sample}{more}; use --force to replace them")
|
|
188
|
+
|
|
189
|
+
for src in sources:
|
|
190
|
+
dst = skills_dst / src.name
|
|
191
|
+
if dst in identical_existing:
|
|
192
|
+
installed.append(str(dst.relative_to(target)))
|
|
193
|
+
continue
|
|
194
|
+
if dst.exists() or dst.is_symlink():
|
|
195
|
+
if dst.is_symlink() or dst.is_file():
|
|
196
|
+
dst.unlink()
|
|
197
|
+
else:
|
|
198
|
+
shutil.rmtree(dst)
|
|
199
|
+
if mode == "link":
|
|
200
|
+
dst.symlink_to(src, target_is_directory=True)
|
|
201
|
+
else:
|
|
202
|
+
shutil.copytree(src, dst)
|
|
203
|
+
installed.append(str(dst.relative_to(target)))
|
|
204
|
+
|
|
205
|
+
instruction_files = _instruction_files(agents)
|
|
206
|
+
for rel in instruction_files:
|
|
207
|
+
merge_block(target / rel)
|
|
208
|
+
|
|
209
|
+
if manage_gitignore:
|
|
210
|
+
merge_gitignore(target / ".gitignore")
|
|
211
|
+
|
|
212
|
+
checksums = {rel: _hash_path(target / rel) for rel in installed} if mode == "copy" else {}
|
|
213
|
+
tree_checksum = _hash_path(skills_dst) if mode == "copy" else None # legacy manifest compatibility
|
|
214
|
+
meta = {
|
|
215
|
+
"source": str(REPO),
|
|
216
|
+
"agent_selection": agent_selection,
|
|
217
|
+
"agents": agents,
|
|
218
|
+
"mode": mode,
|
|
219
|
+
"skill_dir": str(skills_dst.relative_to(target)),
|
|
220
|
+
"instruction_files": instruction_files,
|
|
221
|
+
"gitignore_managed": manage_gitignore,
|
|
222
|
+
"installed": installed,
|
|
223
|
+
"checksums": checksums,
|
|
224
|
+
"tree_checksum": tree_checksum,
|
|
225
|
+
}
|
|
226
|
+
mp = target / MANIFEST
|
|
227
|
+
mp.parent.mkdir(parents=True, exist_ok=True)
|
|
228
|
+
mp.write_text(json.dumps(meta, indent=2) + "\n", encoding="utf-8")
|
|
229
|
+
|
|
230
|
+
print(f"installed {len(installed)} skills in {target}")
|
|
231
|
+
print(f"compatibility: {agent_selection} ({', '.join(agents)})")
|
|
232
|
+
print(f"mode: {mode}")
|
|
233
|
+
print("instructions: " + ", ".join(instruction_files))
|
|
234
|
+
print(f"skills: {skills_dst.relative_to(target)}")
|
|
235
|
+
print("ready: ask your agent to use poteto-mode")
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def install(args):
|
|
239
|
+
target = Path(args.target).resolve()
|
|
240
|
+
mode = "link" if args.link else "copy"
|
|
241
|
+
_install_impl(
|
|
242
|
+
target=target,
|
|
243
|
+
agent_selection=args.agent,
|
|
244
|
+
mode=mode,
|
|
245
|
+
force=args.force,
|
|
246
|
+
manage_gitignore=not args.no_gitignore,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _load_manifest(target: Path) -> tuple[Path, dict]:
|
|
251
|
+
mp = target / MANIFEST
|
|
252
|
+
if not mp.exists():
|
|
253
|
+
die(f"no install manifest at {mp}")
|
|
254
|
+
try:
|
|
255
|
+
return mp, json.loads(mp.read_text(encoding="utf-8"))
|
|
256
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
257
|
+
die(f"cannot read install manifest: {exc}")
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _modified_managed_paths(target: Path, meta: dict) -> list[str]:
|
|
261
|
+
installed = meta.get("installed", [])
|
|
262
|
+
mode = meta.get("mode", "copy")
|
|
263
|
+
if mode == "link":
|
|
264
|
+
source_root = Path(meta.get("source", REPO)) / "skills"
|
|
265
|
+
modified = []
|
|
266
|
+
for rel in installed:
|
|
267
|
+
path = target / rel
|
|
268
|
+
if not path.is_symlink():
|
|
269
|
+
modified.append(rel + (" (missing)" if not path.exists() else " (not a symlink)"))
|
|
270
|
+
continue
|
|
271
|
+
expected = os.path.normpath(str(source_root / Path(rel).name))
|
|
272
|
+
actual_target = os.readlink(path)
|
|
273
|
+
actual = os.path.normpath(
|
|
274
|
+
actual_target if os.path.isabs(actual_target) else str(path.parent / actual_target)
|
|
275
|
+
)
|
|
276
|
+
if actual != expected:
|
|
277
|
+
modified.append(rel + " (link target changed)")
|
|
278
|
+
return modified
|
|
279
|
+
|
|
280
|
+
skill_dir = target / meta.get("skill_dir", ".agents/skills")
|
|
281
|
+
if not skill_dir.exists():
|
|
282
|
+
return [str(skill_dir.relative_to(target)) + " (missing)"]
|
|
283
|
+
|
|
284
|
+
checksums = meta.get("checksums", {})
|
|
285
|
+
if checksums:
|
|
286
|
+
modified = []
|
|
287
|
+
for rel in installed:
|
|
288
|
+
path = target / rel
|
|
289
|
+
expected = checksums.get(rel)
|
|
290
|
+
if not path.exists() and not path.is_symlink():
|
|
291
|
+
modified.append(rel + " (missing)")
|
|
292
|
+
elif expected and _hash_path(path) != expected:
|
|
293
|
+
modified.append(rel)
|
|
294
|
+
return modified
|
|
295
|
+
|
|
296
|
+
# Backward compatibility for manifests that only tracked the whole skill tree.
|
|
297
|
+
expected_tree = meta.get("tree_checksum")
|
|
298
|
+
if expected_tree:
|
|
299
|
+
return [] if _hash_path(skill_dir) == expected_tree else [meta.get("skill_dir", ".agents/skills") + " (content changed)"]
|
|
300
|
+
|
|
301
|
+
modified = []
|
|
302
|
+
for rel in installed:
|
|
303
|
+
path = target / rel
|
|
304
|
+
if not path.exists() and not path.is_symlink():
|
|
305
|
+
modified.append(rel + " (missing)")
|
|
306
|
+
return modified
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def update(args):
|
|
310
|
+
target = Path(args.target).resolve()
|
|
311
|
+
_, meta = _load_manifest(target)
|
|
312
|
+
modified = _modified_managed_paths(target, meta)
|
|
313
|
+
if modified and not args.force:
|
|
314
|
+
sample = ", ".join(modified[:5])
|
|
315
|
+
more = f" (+{len(modified) - 5} more)" if len(modified) > 5 else ""
|
|
316
|
+
die(f"managed skills were edited or removed: {sample}{more}; rerun with --force to replace them")
|
|
317
|
+
|
|
318
|
+
for rel in meta.get("installed", []):
|
|
319
|
+
path = target / rel
|
|
320
|
+
if path.is_symlink() or path.is_file():
|
|
321
|
+
path.unlink(missing_ok=True)
|
|
322
|
+
elif path.exists():
|
|
323
|
+
shutil.rmtree(path)
|
|
324
|
+
|
|
325
|
+
agent_selection = meta.get("agent_selection")
|
|
326
|
+
if not agent_selection:
|
|
327
|
+
old_agent = meta.get("agent", "universal")
|
|
328
|
+
agent_selection = old_agent if old_agent in REGISTRY else "universal"
|
|
329
|
+
update_mode = "copy" if args.copy else "link" if args.link else meta.get("mode", "copy")
|
|
330
|
+
_install_impl(
|
|
331
|
+
target=target,
|
|
332
|
+
agent_selection=agent_selection,
|
|
333
|
+
mode=update_mode,
|
|
334
|
+
force=False,
|
|
335
|
+
manage_gitignore=bool(meta.get("gitignore_managed", True)),
|
|
336
|
+
)
|
|
337
|
+
print("updated potetos-for-everyone")
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def uninstall(args):
|
|
341
|
+
target = Path(args.target).resolve()
|
|
342
|
+
mp, meta = _load_manifest(target)
|
|
343
|
+
for rel in meta.get("installed", []):
|
|
344
|
+
path = target / rel
|
|
345
|
+
if path.is_symlink() or path.is_file():
|
|
346
|
+
path.unlink(missing_ok=True)
|
|
347
|
+
elif path.exists():
|
|
348
|
+
shutil.rmtree(path)
|
|
349
|
+
skill_dir = target / meta.get("skill_dir", ".agents/skills")
|
|
350
|
+
_prune_empty_parents(skill_dir, target)
|
|
351
|
+
for rel in meta.get("instruction_files", ["AGENTS.md"]):
|
|
352
|
+
instruction = target / rel
|
|
353
|
+
remove_block(instruction)
|
|
354
|
+
if not instruction.exists():
|
|
355
|
+
_prune_empty_parents(instruction.parent, target)
|
|
356
|
+
if meta.get("gitignore_managed"):
|
|
357
|
+
remove_gitignore(target / ".gitignore")
|
|
358
|
+
mp.unlink(missing_ok=True)
|
|
359
|
+
_prune_empty_parents(mp.parent, target)
|
|
360
|
+
print(f"uninstalled potetos-for-everyone from {target}")
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def list_skills(_args):
|
|
364
|
+
for path in sorted((REPO / "skills").iterdir()):
|
|
365
|
+
if (path / "SKILL.md").exists():
|
|
366
|
+
print(path.name)
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def status(args):
|
|
370
|
+
target = Path(args.target).resolve()
|
|
371
|
+
mp = target / MANIFEST
|
|
372
|
+
if not mp.exists():
|
|
373
|
+
print(f"not installed in {target}")
|
|
374
|
+
raise SystemExit(1)
|
|
375
|
+
_, meta = _load_manifest(target)
|
|
376
|
+
modified = _modified_managed_paths(target, meta)
|
|
377
|
+
installed = len(meta.get("installed", []))
|
|
378
|
+
print(f"installed: {installed} skills")
|
|
379
|
+
print(f"compatibility: {meta.get('agent_selection', ','.join(meta.get('agents', [])) or 'unknown')}")
|
|
380
|
+
print(f"mode: {meta.get('mode', 'unknown')}")
|
|
381
|
+
if modified:
|
|
382
|
+
print(f"state: modified ({len(modified)} managed paths differ)")
|
|
383
|
+
for rel in modified[:10]:
|
|
384
|
+
print(f" {rel}")
|
|
385
|
+
raise SystemExit(1)
|
|
386
|
+
print("state: clean")
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def doctor(args):
|
|
390
|
+
target = Path(args.target).resolve()
|
|
391
|
+
print(f"repo: {REPO}")
|
|
392
|
+
print(f"target: {target} ({'ok' if target.exists() else 'missing'})")
|
|
393
|
+
print(f"python: {sys.version.split()[0]}")
|
|
394
|
+
canonical = sum(1 for p in (REPO / "skills").iterdir() if (p / "SKILL.md").exists())
|
|
395
|
+
print(f"canonical skills: {canonical}")
|
|
396
|
+
print("native Agent Skills path: .agents/skills/")
|
|
397
|
+
if (target / MANIFEST).exists():
|
|
398
|
+
try:
|
|
399
|
+
_, meta = _load_manifest(target)
|
|
400
|
+
modified = _modified_managed_paths(target, meta)
|
|
401
|
+
print(f"install: {len(meta.get('installed', []))} managed skills, {'modified' if modified else 'clean'}")
|
|
402
|
+
except SystemExit:
|
|
403
|
+
pass
|
|
404
|
+
else:
|
|
405
|
+
print("install: not installed")
|
|
406
|
+
print("adapters:")
|
|
407
|
+
for name, spec in sorted(REGISTRY.items()):
|
|
408
|
+
found = (target / spec["instruction_file"]).exists() if target.exists() else False
|
|
409
|
+
print(
|
|
410
|
+
f" {name:10} skills={spec['skill_dir']:16} instructions={spec['instruction_file']}"
|
|
411
|
+
+ (" [present]" if found else "")
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _prompt_from_args(args):
|
|
416
|
+
if bool(args.prompt) == bool(args.prompt_file):
|
|
417
|
+
die("provide exactly one of --prompt or --prompt-file")
|
|
418
|
+
if args.prompt is not None:
|
|
419
|
+
return args.prompt
|
|
420
|
+
return Path(args.prompt_file).read_text(encoding="utf-8")
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def _runner_config(args, workspace: Path):
|
|
424
|
+
path = Path(args.config).resolve() if args.config else workspace / ".potetos/config.json"
|
|
425
|
+
try:
|
|
426
|
+
return load_config(path)
|
|
427
|
+
except (OSError, ValueError, json.JSONDecodeError) as exc:
|
|
428
|
+
die(str(exc))
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def delegate(args):
|
|
432
|
+
workspace = Path(args.workspace).resolve()
|
|
433
|
+
prompt = _prompt_from_args(args)
|
|
434
|
+
config = _runner_config(args, workspace)
|
|
435
|
+
runs_dir = Path(args.runs_dir).resolve() if args.runs_dir else workspace / ".potetos/runs"
|
|
436
|
+
try:
|
|
437
|
+
result = run_worker(
|
|
438
|
+
config=config,
|
|
439
|
+
runner=args.runner,
|
|
440
|
+
prompt=prompt,
|
|
441
|
+
workspace=workspace,
|
|
442
|
+
runs_dir=runs_dir,
|
|
443
|
+
isolate=args.isolate,
|
|
444
|
+
)
|
|
445
|
+
except (OSError, RuntimeError, ValueError, KeyError) as exc:
|
|
446
|
+
die(str(exc))
|
|
447
|
+
print(json.dumps(result.__dict__, indent=2))
|
|
448
|
+
raise SystemExit(0 if result.returncode == 0 else result.returncode)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def panel(args):
|
|
452
|
+
workspace = Path(args.workspace).resolve()
|
|
453
|
+
prompt = _prompt_from_args(args)
|
|
454
|
+
config = _runner_config(args, workspace)
|
|
455
|
+
names = [x.strip() for x in args.runners.split(",") if x.strip()] if args.runners else list(config["runners"])
|
|
456
|
+
runs_dir = Path(args.runs_dir).resolve() if args.runs_dir else workspace / ".potetos/runs"
|
|
457
|
+
try:
|
|
458
|
+
run_id, results = run_panel(
|
|
459
|
+
config=config,
|
|
460
|
+
runner_names=names,
|
|
461
|
+
prompt=prompt,
|
|
462
|
+
workspace=workspace,
|
|
463
|
+
runs_dir=runs_dir,
|
|
464
|
+
isolate=args.isolate,
|
|
465
|
+
max_parallel=args.max_parallel,
|
|
466
|
+
)
|
|
467
|
+
except (OSError, RuntimeError, ValueError, KeyError) as exc:
|
|
468
|
+
die(str(exc))
|
|
469
|
+
payload = {"run_id": run_id, "results": [r.__dict__ for r in results]}
|
|
470
|
+
print(json.dumps(payload, indent=2))
|
|
471
|
+
if any(r.returncode for r in results):
|
|
472
|
+
raise SystemExit(1)
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def main():
|
|
476
|
+
if sys.version_info < (3, 10):
|
|
477
|
+
die("Python 3.10 or newer is required")
|
|
478
|
+
ap = argparse.ArgumentParser(
|
|
479
|
+
prog="potetos",
|
|
480
|
+
description="Install Lauren Tan's pstack engineering workflow as portable Agent Skills.",
|
|
481
|
+
)
|
|
482
|
+
sub = ap.add_subparsers(dest="cmd", required=True)
|
|
483
|
+
|
|
484
|
+
p = sub.add_parser("install", help="install the skills into a project")
|
|
485
|
+
p.add_argument("--target", default=".")
|
|
486
|
+
p.add_argument("--agent", default="all", choices=["all"] + sorted(REGISTRY))
|
|
487
|
+
mode = p.add_mutually_exclusive_group()
|
|
488
|
+
mode.add_argument("--copy", action="store_true", help="copy skills (default; self-contained and remote-safe)")
|
|
489
|
+
mode.add_argument("--link", action="store_true", help="symlink skills for local development")
|
|
490
|
+
p.add_argument("--force", action="store_true", help="replace colliding skill directories")
|
|
491
|
+
p.add_argument("--no-gitignore", action="store_true", help="do not add the local .potetos/ state directory to .gitignore")
|
|
492
|
+
p.set_defaults(fn=install)
|
|
493
|
+
|
|
494
|
+
p = sub.add_parser("update", help="refresh an existing managed install without clobbering local edits")
|
|
495
|
+
p.add_argument("--target", default=".")
|
|
496
|
+
p.add_argument("--force", action="store_true", help="replace managed skills even if they were edited")
|
|
497
|
+
update_mode = p.add_mutually_exclusive_group()
|
|
498
|
+
update_mode.add_argument("--copy", action="store_true", help="migrate the managed install to self-contained copies")
|
|
499
|
+
update_mode.add_argument("--link", action="store_true", help="migrate the managed install to local-development symlinks")
|
|
500
|
+
p.set_defaults(fn=update)
|
|
501
|
+
|
|
502
|
+
p = sub.add_parser("uninstall", help="remove only files managed by potetos-for-everyone")
|
|
503
|
+
p.add_argument("--target", default=".")
|
|
504
|
+
p.set_defaults(fn=uninstall)
|
|
505
|
+
|
|
506
|
+
p = sub.add_parser("status", help="show whether an install is present and clean")
|
|
507
|
+
p.add_argument("--target", default=".")
|
|
508
|
+
p.set_defaults(fn=status)
|
|
509
|
+
|
|
510
|
+
p = sub.add_parser("list")
|
|
511
|
+
p.set_defaults(fn=list_skills)
|
|
512
|
+
|
|
513
|
+
p = sub.add_parser("doctor")
|
|
514
|
+
p.add_argument("--target", default=".")
|
|
515
|
+
p.set_defaults(fn=doctor)
|
|
516
|
+
|
|
517
|
+
p = sub.add_parser("delegate", help="run one configured external AI agent as an isolated or in-place worker")
|
|
518
|
+
p.add_argument("--workspace", default=".")
|
|
519
|
+
p.add_argument("--runner", default="default")
|
|
520
|
+
p.add_argument("--config")
|
|
521
|
+
p.add_argument("--prompt")
|
|
522
|
+
p.add_argument("--prompt-file")
|
|
523
|
+
p.add_argument("--runs-dir")
|
|
524
|
+
p.add_argument("--isolate", action="store_true", help="create a clean git worktree and branch for the worker")
|
|
525
|
+
p.set_defaults(fn=delegate)
|
|
526
|
+
|
|
527
|
+
p = sub.add_parser("panel", help="fan the same prompt out to configured external AI agents")
|
|
528
|
+
p.add_argument("--workspace", default=".")
|
|
529
|
+
p.add_argument("--runners", help="comma-separated runner profile names; defaults to all configured runners")
|
|
530
|
+
p.add_argument("--config")
|
|
531
|
+
p.add_argument("--prompt")
|
|
532
|
+
p.add_argument("--prompt-file")
|
|
533
|
+
p.add_argument("--runs-dir")
|
|
534
|
+
p.add_argument("--isolate", action="store_true", help="give every runner its own clean git worktree and branch")
|
|
535
|
+
p.add_argument("--max-parallel", type=int)
|
|
536
|
+
p.set_defaults(fn=panel)
|
|
537
|
+
|
|
538
|
+
if len(sys.argv) == 1:
|
|
539
|
+
manifest = Path(".").resolve() / MANIFEST
|
|
540
|
+
sys.argv.append("update" if manifest.exists() else "install")
|
|
541
|
+
|
|
542
|
+
args = ap.parse_args()
|
|
543
|
+
args.fn(args)
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
if __name__ == "__main__":
|
|
547
|
+
main()
|
package/docs/.nojekyll
ADDED
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# 1. Setup
|
|
2
|
+
|
|
3
|
+
The lowest-friction install is from the target repository itself:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
curl -fsSL https://raw.githubusercontent.com/TheOnlyFusionCube/potetos-for-everyone/main/install.sh | sh
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
PowerShell:
|
|
10
|
+
|
|
11
|
+
```powershell
|
|
12
|
+
irm https://raw.githubusercontent.com/TheOnlyFusionCube/potetos-for-everyone/main/install.ps1 | iex
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The default copies the canonical skills into `.agents/skills/` and writes all supported tiny instruction shims, preserving any content already in those files. This makes the result self-contained and usable by different agents without reinstalling.
|
|
16
|
+
|
|
17
|
+
If you are developing `potetos-for-everyone` itself from a local clone, use links instead:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
python3 /path/to/potetos-for-everyone/bin/potetos install --target /path/to/project --link --agent universal
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Re-running the one-line installer updates a clean managed install. It refuses to overwrite locally edited managed skills unless explicitly forced.
|
|
24
|
+
|
|
25
|
+
After installation, ask the host to **use `poteto-mode`** on a small real task. If native skill discovery is unavailable, the generated instruction shim points directly to `.agents/skills/poteto-mode/SKILL.md`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# 2. Poteto Mode
|
|
2
|
+
|
|
3
|
+
`poteto-mode` is the front door. It classifies a task into a playbook, copies that playbook's steps into whatever task-tracking mechanism exists, and progressively loads supporting skills.
|
|
4
|
+
|
|
5
|
+
A useful prompt is outcome-first:
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
Use poteto-mode. Reproduce the idle scroll drift, fix the root cause with the smallest change, and prove the real UI no longer moves while idle.
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Do not manually invoke every principle. The router loads a principle only when it changes a concrete decision. This protects context and keeps rigor from becoming ceremony.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# 3. Understand before editing
|
|
2
|
+
|
|
3
|
+
Use `how` for mechanism and `why` for rationale. Mechanism comes from current code/runtime evidence. Rationale comes from contemporaneous history such as commits, PRs, issues, docs, or chat when the host can access it.
|
|
4
|
+
|
|
5
|
+
Keep facts and inference separate. When an explanation hinges on one fragile assumption, test that assumption before editing. For a defect, the bug-fix playbook requires a reproduction or an explicit statement that the reproduction surface is unavailable.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# 4. Design boundaries
|
|
2
|
+
|
|
3
|
+
Load `architect` when a change crosses a meaningful function, module, process, storage, or network boundary. Start from the caller and settle the data shape, ownership, lifecycle, and invalid states before implementation.
|
|
4
|
+
|
|
5
|
+
For uncertain designs, use `arena` or `prototype`: compare materially different options against one frozen rubric. True isolated workers are preferred when the host supports them. Sequential alternatives remain useful, but they are not independent evidence.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# 5. Build and clean
|
|
2
|
+
|
|
3
|
+
Build the smallest end-to-end slice that produces the requested outcome. Prefer deletion, direct data shapes, and migration of callers over compatibility scaffolding that becomes permanent by accident.
|
|
4
|
+
|
|
5
|
+
Use `tdd` when a cheap behavior-level failing test exists. Use `no-comments` before review to remove comments that only narrate syntax; encode durable constraints in types, tests, assertions, lints, or structure when possible.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# 6. Verify and ship
|
|
2
|
+
|
|
3
|
+
Completion means proof against the requested surface, not merely a plausible diff. Unit tests prove unit behavior; a compile proves compilation; neither automatically proves UI, service, device, or workflow behavior.
|
|
4
|
+
|
|
5
|
+
Use `interrogate` for adversarial review, `blast-radius` to prove what else can be affected, and `shipping` when a verified PR/stack is meant to land. Freeze the exact revision being judged and report the actual commands, artifacts, and unavailable verification surfaces.
|