@bitkentech/shipsmooth-opencode 0.3.27
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/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bitkentech/shipsmooth-opencode",
|
|
3
|
+
"version": "0.3.27",
|
|
4
|
+
"description": "Agent coding workflow with plan-before-implement discipline, TDD, vertical slices, Linear integration, and immutable git-based plan versioning.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "plugin/index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"plugin/",
|
|
9
|
+
"skills/"
|
|
10
|
+
],
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Pramod Biligiri"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/bitkentech/shipsmooth",
|
|
18
|
+
"repository": "https://github.com/bitkentech/shipsmooth",
|
|
19
|
+
"license": "Apache-2.0"
|
|
20
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Node-free SessionStart bootstrap for the shipsmooth jlink runtime.
|
|
3
|
+
#
|
|
4
|
+
# Invoked by the plugin's SessionStart hook as:
|
|
5
|
+
# sh ".../hooks/install-shipsmooth.sh" <plugin-name> <version>
|
|
6
|
+
#
|
|
7
|
+
# Strict POSIX sh using only the stock-macOS toolset (sh, curl, unzip, uname,
|
|
8
|
+
# chmod, mktemp, mv): detect platform, skip if already installed, download the
|
|
9
|
+
# release zip, unzip (Info-ZIP restores the stored +x on runtime/lib/jspawnhelper
|
|
10
|
+
# that OpenJ9 needs to spawn subprocesses), force-chmod the launcher as a
|
|
11
|
+
# backstop, then atomically mv into place. Mirrors session-start.ts.
|
|
12
|
+
#
|
|
13
|
+
# Env override (tests): SS_URL_BASE replaces the GitHub releases base URL.
|
|
14
|
+
set -eu
|
|
15
|
+
|
|
16
|
+
NAME="${1:?usage: install-shipsmooth.sh <plugin-name> <version>}"
|
|
17
|
+
VERSION="${2:?usage: install-shipsmooth.sh <plugin-name> <version>}"
|
|
18
|
+
|
|
19
|
+
CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/$NAME"
|
|
20
|
+
URL_BASE="${SS_URL_BASE:-https://github.com/bitkentech/shipsmooth/releases/download/v$VERSION}"
|
|
21
|
+
|
|
22
|
+
log() { printf '%s: %s\n' "$NAME" "$1" >&2; }
|
|
23
|
+
die() { log "$1"; exit 1; }
|
|
24
|
+
|
|
25
|
+
os=$(uname -s)
|
|
26
|
+
arch=$(uname -m)
|
|
27
|
+
case "$os" in
|
|
28
|
+
Darwin) os=darwin ;;
|
|
29
|
+
Linux) os=linux ;;
|
|
30
|
+
*) die "unsupported OS: $os" ;;
|
|
31
|
+
esac
|
|
32
|
+
case "$arch" in
|
|
33
|
+
arm64|aarch64) arch=arm64 ;;
|
|
34
|
+
x86_64|amd64) arch=x64 ;;
|
|
35
|
+
*) die "unsupported arch: $arch" ;;
|
|
36
|
+
esac
|
|
37
|
+
PLATFORM="$os-$arch"
|
|
38
|
+
|
|
39
|
+
RUNTIME_DIR="$CACHE_DIR/$VERSION"
|
|
40
|
+
BIN="$RUNTIME_DIR/bin/shipsmooth"
|
|
41
|
+
if [ -x "$BIN" ]; then
|
|
42
|
+
log "runtime $VERSION already installed at $RUNTIME_DIR"
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
case "$PLATFORM" in
|
|
47
|
+
linux-x64|darwin-x64|darwin-arm64) : ;;
|
|
48
|
+
*) die "platform $PLATFORM is not supported" ;;
|
|
49
|
+
esac
|
|
50
|
+
|
|
51
|
+
TMP=$(mktemp -d "${TMPDIR:-/tmp}/$NAME-XXXXXX")
|
|
52
|
+
EXTRACT_DIR="$RUNTIME_DIR.tmp"
|
|
53
|
+
trap 'rm -rf "$TMP" "$EXTRACT_DIR"' EXIT INT TERM
|
|
54
|
+
|
|
55
|
+
ZIP="$TMP/runtime.zip"
|
|
56
|
+
URL="$URL_BASE/$NAME-$VERSION-$PLATFORM.zip"
|
|
57
|
+
log "downloading runtime from $URL"
|
|
58
|
+
curl -fsSL --retry 2 --retry-delay 1 -A "$NAME-runtime-installer" -o "$ZIP" "$URL" \
|
|
59
|
+
|| die "failed to download $URL"
|
|
60
|
+
|
|
61
|
+
rm -rf "$EXTRACT_DIR"
|
|
62
|
+
mkdir -p "$EXTRACT_DIR"
|
|
63
|
+
unzip -q "$ZIP" -d "$EXTRACT_DIR"
|
|
64
|
+
|
|
65
|
+
EXTRACTED_BIN="$EXTRACT_DIR/bin/shipsmooth"
|
|
66
|
+
[ -f "$EXTRACTED_BIN" ] || die "extracted archive is missing bin/shipsmooth"
|
|
67
|
+
chmod 0755 "$EXTRACTED_BIN"
|
|
68
|
+
|
|
69
|
+
rm -rf "$RUNTIME_DIR"
|
|
70
|
+
mv "$EXTRACT_DIR" "$RUNTIME_DIR"
|
|
71
|
+
log "runtime $VERSION installed at $RUNTIME_DIR"
|
package/plugin/index.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// shipsmooth OpenCode plugin entry (plan-86 Task 10/11).
|
|
2
|
+
//
|
|
3
|
+
// The only executable code shipsmooth ships to any host. Two jobs, both proven
|
|
4
|
+
// in the Task 1 de-risk:
|
|
5
|
+
// 1. Bootstrap the jlink runtime on the first `session.created` event by
|
|
6
|
+
// shelling out (via Bun's `$`) to the bundled hooks/install-shipsmooth.sh.
|
|
7
|
+
// Idempotent + non-fatal: a failure logs and degrades to "CLI unavailable",
|
|
8
|
+
// never crashing the session.
|
|
9
|
+
// 2. Register the `shipsmooth:start` slash command whose template is a thin
|
|
10
|
+
// pointer to the native `start` SKILL.md (the single-sourced workflow text).
|
|
11
|
+
//
|
|
12
|
+
// Authored in TS, shipped as plain transpiled .js (Task 4). `@opencode-ai/plugin`
|
|
13
|
+
// is a type-only devDependency.
|
|
14
|
+
//
|
|
15
|
+
// IMPORTANT (Task 11 integration finding). OpenCode 1.17.9 discovers plugins by
|
|
16
|
+
// scanning `<config-dir>/plugin/*.js` (NON-recursively) and, for each file, calls
|
|
17
|
+
// EVERY export as a plugin factory (`for (const v of Object.values(mod))`). Two
|
|
18
|
+
// consequences this module is shaped around:
|
|
19
|
+
// 1. The entry exports ONLY the factory (named + default). Exporting a helper
|
|
20
|
+
// here would make OpenCode call e.g. readConfig(context) → it path.joins the
|
|
21
|
+
// context object → plugin load crashes with `"paths[0]" ... got object`.
|
|
22
|
+
// 2. The pure helpers live in ./lib/internal.js — under plugin/lib/, NOT
|
|
23
|
+
// plugin/, so OpenCode's non-recursive scan never loads them as a plugin (a
|
|
24
|
+
// sibling plugin/internal.js WOULD be loaded and rejected as
|
|
25
|
+
// "Plugin export is not a function" for its non-function exports).
|
|
26
|
+
//
|
|
27
|
+
// The version + skill name come from the rendered config JSON next to the
|
|
28
|
+
// compiled module (dist/session-start-config.json), so a version bump re-renders
|
|
29
|
+
// one file and this source is untouched.
|
|
30
|
+
import { existsSync } from "node:fs";
|
|
31
|
+
import { moduleDir, readConfig, startCommandId, startCommandTemplate, installerPath, safeLog, } from "./lib/internal.js";
|
|
32
|
+
export const ShipsmoothPlugin = async ({ client, $ }) => {
|
|
33
|
+
const base = moduleDir(import.meta.url);
|
|
34
|
+
let cfg;
|
|
35
|
+
try {
|
|
36
|
+
cfg = readConfig(base);
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
// Without config we cannot bootstrap; log and return an inert plugin.
|
|
40
|
+
await safeLog(client, "error", `shipsmooth: ${e.message}`);
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
let bootstrapped = false;
|
|
44
|
+
async function bootstrap() {
|
|
45
|
+
if (bootstrapped)
|
|
46
|
+
return;
|
|
47
|
+
bootstrapped = true;
|
|
48
|
+
const installer = installerPath(base);
|
|
49
|
+
if (!existsSync(installer)) {
|
|
50
|
+
await safeLog(client, "error", `shipsmooth: installer not found at ${installer}`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
// Idempotent: the script early-exits if the runtime is already installed.
|
|
55
|
+
const res = await $ `sh ${installer} ${cfg.name} ${cfg.version}`.nothrow().quiet();
|
|
56
|
+
if (res.exitCode !== 0) {
|
|
57
|
+
await safeLog(client, "error", `shipsmooth: runtime bootstrap exit ${res.exitCode}: ${res.stderr.toString().trim()}`);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
await safeLog(client, "info", `shipsmooth: runtime ${cfg.version} ready`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
// Non-fatal: a missing runtime degrades to "CLI unavailable", never crashes.
|
|
65
|
+
await safeLog(client, "error", `shipsmooth: bootstrap failed: ${e.message}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const hooks = {
|
|
69
|
+
config: async (config) => {
|
|
70
|
+
config.command = config.command ?? {};
|
|
71
|
+
config.command[startCommandId(cfg.name)] = {
|
|
72
|
+
description: "Apply the shipsmooth agent coding workflow",
|
|
73
|
+
template: startCommandTemplate(cfg.name),
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
event: async ({ event }) => {
|
|
77
|
+
if (event?.type === "session.created") {
|
|
78
|
+
await bootstrap();
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
return hooks;
|
|
83
|
+
};
|
|
84
|
+
export default ShipsmoothPlugin;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// shipsmooth OpenCode plugin — internal helpers (plan-86 Task 10/11).
|
|
2
|
+
//
|
|
3
|
+
// These pure helpers live OUTSIDE the plugin entry module on purpose. OpenCode
|
|
4
|
+
// (verified on 1.17.9) loads a plugin file by importing it and treating EVERY
|
|
5
|
+
// named export as a plugin factory — it invokes each export with the plugin
|
|
6
|
+
// context object. Helpers like `readConfig`/`installerPath` call `path.join` on
|
|
7
|
+
// their first argument, so if OpenCode called them with the context object the
|
|
8
|
+
// join throws `"paths[0]" must be of type string, got object` and the plugin
|
|
9
|
+
// "fails to load". Keeping them here (imported by index.ts, never re-exported)
|
|
10
|
+
// means the entry module exposes only the real factory. The unit suite imports
|
|
11
|
+
// these directly from this module.
|
|
12
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
13
|
+
import { dirname, join } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
export const CONFIG_FILE = "session-start-config.json";
|
|
16
|
+
/** Resolve the directory a compiled module lives in (pass `import.meta.url`). */
|
|
17
|
+
export function moduleDir(metaUrl) {
|
|
18
|
+
return dirname(fileURLToPath(metaUrl));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Read {name, version} from the rendered config JSON. The renderer writes it to
|
|
22
|
+
* `dist/session-start-config.json`; the compiled plugin lives next to that dist
|
|
23
|
+
* dir, so we look in the module dir and its `dist` sibling. Throws if absent —
|
|
24
|
+
* the payload is malformed without it.
|
|
25
|
+
*/
|
|
26
|
+
export function readConfig(baseDir) {
|
|
27
|
+
const candidates = [
|
|
28
|
+
join(baseDir, CONFIG_FILE),
|
|
29
|
+
join(baseDir, "dist", CONFIG_FILE),
|
|
30
|
+
];
|
|
31
|
+
for (const c of candidates) {
|
|
32
|
+
if (existsSync(c)) {
|
|
33
|
+
const parsed = JSON.parse(readFileSync(c, "utf-8"));
|
|
34
|
+
return { name: parsed.name, version: parsed.version };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
throw new Error(`shipsmooth: ${CONFIG_FILE} not found near ${baseDir}`);
|
|
38
|
+
}
|
|
39
|
+
/** The skill the start command delegates to: `start` (prod) or `start-dev` (dev). */
|
|
40
|
+
export function skillName(pluginName) {
|
|
41
|
+
return pluginName.endsWith("-dev") ? "start-dev" : "start";
|
|
42
|
+
}
|
|
43
|
+
/** Thin launcher template — points the agent at the canonical skill, no inlined workflow. */
|
|
44
|
+
export function startCommandTemplate(pluginName) {
|
|
45
|
+
const skill = skillName(pluginName);
|
|
46
|
+
return (`Apply the shipsmooth agent coding workflow. Invoke the \`${skill}\` skill ` +
|
|
47
|
+
`(via the skill tool) and follow it for this task.`);
|
|
48
|
+
}
|
|
49
|
+
/** Command id registered in OpenCode's config.command map. */
|
|
50
|
+
export function startCommandId(pluginName) {
|
|
51
|
+
return `${pluginName}:start`;
|
|
52
|
+
}
|
|
53
|
+
/** Path to the bundled installer the bootstrap shells out to. */
|
|
54
|
+
export function installerPath(baseDir) {
|
|
55
|
+
return join(baseDir, "hooks", "install-shipsmooth.sh");
|
|
56
|
+
}
|
|
57
|
+
/** Best-effort structured log; never throws (logging must not break a session). */
|
|
58
|
+
export async function safeLog(client, level, message) {
|
|
59
|
+
try {
|
|
60
|
+
await client.app.log({ body: { service: "shipsmooth", level, message } });
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// logging is best-effort
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: start
|
|
3
|
+
description: Use when starting any task — applies the shipsmooth agent coding workflow.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# start — Agent Coding Workflow
|
|
7
|
+
|
|
8
|
+
## When to apply this skill
|
|
9
|
+
Apply this skill whenever you are:
|
|
10
|
+
- Starting work on a new feature or task
|
|
11
|
+
- Asked to write, revise, or execute a plan
|
|
12
|
+
- Picking up existing work from Linear
|
|
13
|
+
- Closing out, abandoning, or handing off a plan
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Core Invariants — Never Violate These
|
|
18
|
+
|
|
19
|
+
1. **Features vs Plans are strictly separate.** Feature issues live in the permanent backlog forever. Plan issues live in transient `[agent]` projects and are archived after completion. Never create feature issues inside an `[agent]` project.
|
|
20
|
+
2. **A committed, pushed, human-reviewed plan is the contract.** You execute against it. You do not autonomously modify it.
|
|
21
|
+
3. **Every plan must reference at least one permanent backlog feature issue.** `[Linear]` Create an `[agent]` project linking to it. `[Local]` Record it in the `<backlog-issue>` metadata element of the tasks XML file. If no backlog issue exists, stop and create one before proceeding.
|
|
22
|
+
4. **Task tracking is never the source of truth for plan content.** Git is. Linear (or the local tasks file) tracks task state only.
|
|
23
|
+
5. **Tags are permanent.** Never delete a plan version tag from remote, even on abandonment or squash merge.
|
|
24
|
+
6. **Tests precede implementation.** Write integration test(s) before any task code (Phase 2 preamble), then the unit test for each task before its implementation. Never implement without a failing test already committed. (Apply as far as possible — migrations and config may not be TDD-able.)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Control Strategy: The Risk-Quality Loop
|
|
29
|
+
|
|
30
|
+
To maximize productivity while minimizing "hallucination drift," treat
|
|
31
|
+
risk and quality as two pressures that peak at different times — and never
|
|
32
|
+
chase both at once.
|
|
33
|
+
|
|
34
|
+
- **Spiral risk** — the chance that the architecture or core logic is
|
|
35
|
+
simply wrong. It is highest at the *start* of a task, when the approach
|
|
36
|
+
is unproven, and collapses once the logic is validated.
|
|
37
|
+
- **Implementation quality** — readability, project-pattern conformance,
|
|
38
|
+
and test coverage. It matters only *after* the approach is proven; polishing
|
|
39
|
+
code that may be thrown away is wasted effort.
|
|
40
|
+
|
|
41
|
+
**Strategy:** De-risk aggressively first — prove the logic works and ignore
|
|
42
|
+
quality rules. Once the approach is validated and approved, switch modes and
|
|
43
|
+
harden the code to the quality bar. The per-task **De-risk & Harden Cycle**
|
|
44
|
+
below operationalizes this; this section only explains *why* the two phases
|
|
45
|
+
are kept separate.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Task Tracking Mode
|
|
50
|
+
|
|
51
|
+
This workflow supports two task tracking modes. Choose one at the start of each plan:
|
|
52
|
+
|
|
53
|
+
- **`[Linear]`** — Uses Linear issues and projects. Requires a Linear account and the Linear MCP server configured in Claude Code.
|
|
54
|
+
- **`[Local]`** — Uses a local XML file at `.shipsmooth/plans/plan-{N}-tasks.xml`. No external services required. Requires the plugin's SessionStart hook to have run (downloads the Java CLI runtime to `~/.cache/shipsmooth/`).
|
|
55
|
+
|
|
56
|
+
Throughout this skill, instructions marked `[Linear]` apply only in Linear mode; instructions marked `[Local]` apply only in Local mode. Unmarked instructions apply to both.
|
|
57
|
+
|
|
58
|
+
`[Local]` Script invocations use `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth <subcommand>`. All scripts read/write `.shipsmooth/plans/plan-{N}-tasks.xml` relative to the repo root.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Repository Structure
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
.shipsmooth/
|
|
66
|
+
plans/
|
|
67
|
+
plan-07.md # plan files live here, versioned in git
|
|
68
|
+
plan-07-tasks.xml # [Local] task state (sibling to plan file)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Plans are markdown files. They contain: narrative, design decisions, architecture notes, open questions, and references. Code never goes here.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## What Lives Where — Quick Reference
|
|
76
|
+
|
|
77
|
+
| Content | Location | Reason |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| Plan narrative, design decisions, references | `.shipsmooth/plans/*.md` in git | Needs diffs, version history, co-evolution with code |
|
|
80
|
+
| Task state (done / not done) | `[Linear]` Linear `[agent]` project · `[Local]` `.shipsmooth/plans/plan-{N}-tasks.xml` | Needs status tracking and human review |
|
|
81
|
+
| Feature definitions | `[Linear]` Linear permanent backlog · `[Local]` Noted in plan file Context section | Permanent, human-curated |
|
|
82
|
+
| Link between plan version and tasks | `[Linear]` Tag-based GitHub permalink in Linear issue description · `[Local]` `<created-from>` child element in XML | Immutable, survives branch lifecycle |
|
|
83
|
+
| This workflow | `~/.claude/skills/start/SKILL.md` | Loaded by agent at task start |
|
|
84
|
+
| Repo-specific overrides | `CLAUDE.md` in repo root | Workspace name, project conventions, etc. |
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Git Tagging Convention
|
|
89
|
+
|
|
90
|
+
Every time a plan file is committed, immediately create and push a version tag:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan tag --plan {N} --kind version
|
|
94
|
+
# prints: git push origin plan-{N}-v{K} — run that line to push
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
On clean completion:
|
|
98
|
+
```bash
|
|
99
|
+
${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan tag --plan {N} --kind complete
|
|
100
|
+
# prints: git push origin plan-{N}-complete
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
On abandonment:
|
|
104
|
+
```bash
|
|
105
|
+
${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan tag --plan {N} --kind abandoned
|
|
106
|
+
# prints: git push origin plan-{N}-abandoned
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Tag naming: `plan-{N}-v{version}` for iterations, `plan-{N}-complete` for clean closeout, `plan-{N}-abandoned` for abandonment. `plan tag --kind version` refuses to re-tag if the computed tag already exists — commit more changes first.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Linear Structure
|
|
114
|
+
|
|
115
|
+
`[Linear]` only. Skip this section in Local mode.
|
|
116
|
+
|
|
117
|
+
### Permanent Backlog Project
|
|
118
|
+
- Named e.g. `AppName — Backlog & Roadmap`
|
|
119
|
+
- Contains feature issues only
|
|
120
|
+
- Human-created and human-prioritised
|
|
121
|
+
- Never deleted, survives all plan lifecycles
|
|
122
|
+
|
|
123
|
+
### Transient Agent Projects
|
|
124
|
+
- Named: `[agent] {N} · {short-description}` e.g. `[agent] 07 · home-accounts-settings-bottom-tabs`
|
|
125
|
+
- Created per plan, archived after completion
|
|
126
|
+
- Project description must contain:
|
|
127
|
+
- Link to the permanent backlog feature issue(s) it delivers
|
|
128
|
+
- Permalink to the plan file using the tag-based commit hash URL (see below)
|
|
129
|
+
- Brief plan narrative / design rationale
|
|
130
|
+
|
|
131
|
+
### Tag-based GitHub permalink format
|
|
132
|
+
```
|
|
133
|
+
https://github.com/{org}/{repo}/blob/{tag-commit-hash}/.shipsmooth/plans/plan-07.md
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Resolve the commit hash for a tag:
|
|
137
|
+
```bash
|
|
138
|
+
git rev-list -n 1 plan-07-v1
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Use this hash (not the tag name) in Linear links — it is immutable and survives branch deletion, rebases, and squash merges.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Phase 0 — Intake
|
|
146
|
+
|
|
147
|
+
**First, check for an active plan — do not start a new one on top of it.**
|
|
148
|
+
Before treating any message as a fresh kickoff, look for a plan that is already
|
|
149
|
+
in flight. Glance at the plans on disk and their state — especially the **latest**
|
|
150
|
+
one:
|
|
151
|
+
|
|
152
|
+
- find the plans directory first — in **external** mode (the default) it is not
|
|
153
|
+
`.shipsmooth/plans/` but a separate state dir. Ask the CLI:
|
|
154
|
+
`${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store info --json` reports `plansDir` (when `status` is `ready`).
|
|
155
|
+
List `plansDir`'s `plan-*-tasks.xml` (the highest plan number is the most likely
|
|
156
|
+
candidate); if `status` is **not** `ready`, state is not set up yet — run the
|
|
157
|
+
**first-run handshake** below before going further (there is no active plan to resume).
|
|
158
|
+
- check that plan's state with
|
|
159
|
+
`${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan resume --plan {N}` — a plan-level status of `active` /
|
|
160
|
+
`in-review` with tasks still `pending` / in-progress means work is unfinished.
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
> **First-run handshake `[Local]`.** When a `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth` command reports that state is
|
|
164
|
+
> not set up — `store info --json` returns a `status` other than `ready`, or a
|
|
165
|
+
> state-dependent command exits with a `status:"needs-decision"` / `status:"unresolvable"`
|
|
166
|
+
> JSON line (exit 10 / 11) — do **not** treat it as a normal error. Run this handshake. The
|
|
167
|
+
> CLI never prompts on stdin; presenting the choice and capturing a real human answer is the
|
|
168
|
+
> skill's job.
|
|
169
|
+
>
|
|
170
|
+
> **`needs-decision` → ask the user, then act:**
|
|
171
|
+
>
|
|
172
|
+
> 1. **Show the CLI's `prompt` field verbatim.** The `needs-decision` JSON carries a ready-to-
|
|
173
|
+
> display `prompt` (the question, each option with its real resolved path, the recommended
|
|
174
|
+
> one marked, and — for a clean first run — a note that a different folder may be given).
|
|
175
|
+
> Render it as-is; do not rewrite it or invent paths.
|
|
176
|
+
> 2. **Wait for a real answer. Never auto-pick.** The CLI only ever creates a state location
|
|
177
|
+
> on a fresh human "yes" (consented creation). Default toward the recommended option, but
|
|
178
|
+
> the user may pick the alternative or name a different folder.
|
|
179
|
+
> 3. **Re-invoke the CLI to act** on the choice, matching the `choice` token from the chosen
|
|
180
|
+
> option:
|
|
181
|
+
> ```bash
|
|
182
|
+
> # external (recommended) — accept the proposed folder:
|
|
183
|
+
> ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store init --choice external --json
|
|
184
|
+
> # external — a different folder the user named:
|
|
185
|
+
> ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store init --choice external --path <user's folder> --json
|
|
186
|
+
> # keep it inside this repo:
|
|
187
|
+
> ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store init --choice in-repo --json
|
|
188
|
+
> # a configured external folder went missing — recreate it:
|
|
189
|
+
> ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store init --choice recreate --path <path from the option> --json
|
|
190
|
+
> ```
|
|
191
|
+
> `store init` creates the chosen location, writes the config entry, and prints the
|
|
192
|
+
> `ready` shape — read its `plansDir` for where plan files now live.
|
|
193
|
+
>
|
|
194
|
+
> **`unresolvable` → stop.** The situation cannot be settled automatically (e.g. a legacy
|
|
195
|
+
> `.agents/` tree). Show the JSON `message` to the user and stop; do not try to fix it. The
|
|
196
|
+
> message says what to correct by hand.
|
|
197
|
+
>
|
|
198
|
+
> **Already settled → silent.** When resolution is `ready`, none of this applies; the command
|
|
199
|
+
> just runs. Steady state never re-prompts.
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
If any plan looks active, **surface it as a question** before doing anything
|
|
203
|
+
else: name the plan and ask the user whether to continue it or deliberately
|
|
204
|
+
start a new one. Do not auto-create a new branch or plan file while a plan
|
|
205
|
+
appears to be in flight. *(This is a judgment call for now — there is no single
|
|
206
|
+
deterministic "is any plan active" check; tracked as a known gap. Lean toward
|
|
207
|
+
asking when unsure.)*
|
|
208
|
+
|
|
209
|
+
Once you have confirmed there is no active plan to resume, decide how much
|
|
210
|
+
context you actually have. The kickoff sets the mode for everything that
|
|
211
|
+
follows — choose it deliberately.
|
|
212
|
+
|
|
213
|
+
**The thin-vs-rich test.** Context is **thin** when *all three* hold:
|
|
214
|
+
|
|
215
|
+
- the kickoff message is short (roughly two sentences or fewer), **and**
|
|
216
|
+
- no spec, PRD, or plan body is attached, **and**
|
|
217
|
+
- there is no substantial planning earlier in this conversation.
|
|
218
|
+
|
|
219
|
+
If any one of these is absent, context is **rich** — skip to Phase 1.
|
|
220
|
+
|
|
221
|
+
### Thin context → quickstart, then hand back
|
|
222
|
+
|
|
223
|
+
A short kickoff means the user wants to move fast and iterate. He is signalling
|
|
224
|
+
that he will add detail later or work exploratorily. **Do not slow him down.**
|
|
225
|
+
Run **one** command and hand back:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan quick --desc "{short-description}"
|
|
229
|
+
# derives the next plan number, creates + checks out t/{N}-{slug},
|
|
230
|
+
# and writes a stub .shipsmooth/plans/plan-{N}.md.
|
|
231
|
+
# It does NOT commit — that is intentional.
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Then relay the command's output to the user in one or two lines — the branch and
|
|
235
|
+
stub plan file now exist on the branch for him to flesh out — and **stop, return
|
|
236
|
+
control to the chat.**
|
|
237
|
+
|
|
238
|
+
`plan quick` owns the whole thin-path scaffold: plan-number derivation, branch
|
|
239
|
+
creation, and writing the stub file. **You do not author the plan file or run
|
|
240
|
+
git yourself.** In particular, **do not commit** what `plan quick` wrote — it
|
|
241
|
+
deliberately leaves the stub uncommitted so the user commits on his own terms
|
|
242
|
+
(and so a missing git identity can't strand the quickstart). There is no
|
|
243
|
+
follow-up step after `plan quick` on the thin path.
|
|
244
|
+
|
|
245
|
+
**Do not**, on the thin path:
|
|
246
|
+
|
|
247
|
+
- hand-author the stub plan file, then `git add`/`git commit` it — `plan quick`
|
|
248
|
+
already wrote it and intentionally left it uncommitted; adding a commit is the
|
|
249
|
+
exact mistake this path exists to prevent,
|
|
250
|
+
- run `git commit`, `git tag`, `git push`, or configure git identity,
|
|
251
|
+
- investigate the repository or read source files to "understand the feature",
|
|
252
|
+
- ask clarifying questions or present an options questionnaire,
|
|
253
|
+
- estimate per-task risk, run `plan init`, tag, or set up task tracking.
|
|
254
|
+
|
|
255
|
+
Those belong to the rich-context pass (Phase 1), reached once the user has
|
|
256
|
+
fleshed out the stub.
|
|
257
|
+
|
|
258
|
+
### Worked example (target vs. anti-target)
|
|
259
|
+
|
|
260
|
+
Kickoff: *"start a new plan, feature is X"* — no spec, no prior planning.
|
|
261
|
+
|
|
262
|
+
- ✅ **Target:** run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan quick --desc "X"` → relay its
|
|
263
|
+
output (branch + stub created, uncommitted) → **stop**.
|
|
264
|
+
- ❌ **Anti-target #1:** run several rounds of repo investigation, then fire a
|
|
265
|
+
multi-part questionnaire asking the user to choose the approach, before
|
|
266
|
+
creating anything. This interrogates the user at the moment he wanted to move
|
|
267
|
+
fast. *Do not do this.*
|
|
268
|
+
- ❌ **Anti-target #2:** after `plan quick` (or instead of it), hand-write the
|
|
269
|
+
stub file and `git commit` it. The commit is unrequested git work that can
|
|
270
|
+
fail on an unconfigured identity and strand the flow. *Do not do this.*
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Phase 1 — Plan, Calibrate, & Commit
|
|
275
|
+
|
|
276
|
+
This is the **rich-context** path, reached either directly when kickoff context
|
|
277
|
+
is already rich, or after the user has fleshed out a Phase 0 stub.
|
|
278
|
+
|
|
279
|
+
**You do not write or run any implementation code during this phase.**
|
|
280
|
+
|
|
281
|
+
1. **Draft Plan:** Write or update the plan file at `.shipsmooth/plans/plan-{N}.md`.
|
|
282
|
+
2. **Risk Analysis:**
|
|
283
|
+
- For every task in the plan, suggest a **Default Risk Level** (Low, Medium, or High) with a one-sentence justification.
|
|
284
|
+
3. **Collaborative Calibration:**
|
|
285
|
+
- **Stop.** Ask the human: *"I've estimated these risk levels. Do you want to override any of them?"*
|
|
286
|
+
- The human's choice becomes the **Actual Risk ($R$)**.
|
|
287
|
+
4. **Risk-Sorted Task Ordering:**
|
|
288
|
+
- Re-order tasks in the plan file in **descending order of risk** ($High \to Med \to Low$).
|
|
289
|
+
- *Exception:* If a Low-risk task is a hard technical dependency for a High-risk task, the dependency must come first.
|
|
290
|
+
5. **Commit & Tag:**
|
|
291
|
+
```bash
|
|
292
|
+
git add .shipsmooth/plans/plan-07.md
|
|
293
|
+
git commit -m "plan(07): risk-calibrated plan for [short-description]"
|
|
294
|
+
git push origin t/{issue-id}-{short-description}
|
|
295
|
+
# Lefthook auto-tags plan-07-v1 and pushes it
|
|
296
|
+
```
|
|
297
|
+
6. **Verify Preconditions:**
|
|
298
|
+
```bash
|
|
299
|
+
${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan preflight --plan {N}
|
|
300
|
+
# Exits 0 (PASS) or 1 (FAIL: dirty tree / missing version tag). Warns on unpushed branch.
|
|
301
|
+
```
|
|
302
|
+
7. **Create Task Tracking Infrastructure:**
|
|
303
|
+
- `[Linear]` Create the `[agent]` Linear project. Create Linear issues from the **risk-sorted** plan tasks. Each issue description must include the **Risk Level** ($L/M/H$) and the tag-based GitHub URL of the specific plan version that generated it.
|
|
304
|
+
- `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan init --plan {N} --tasks-from .shipsmooth/plans/plan-{N}.md` to generate `.shipsmooth/plans/plan-{N}-tasks.xml`. Commit the XML file immediately after creation. **Never hand-write this XML file — always generate it via the CLI. The format uses child elements, not attributes.** The CLI requires task headings in the form `### Task N: Name [Risk]` where `N` is a positive integer — alphanumeric IDs (e.g. `01-A`) are not supported. To express a dependency between tasks, add a `*Depends-on: P[,Q...]*` line anywhere in the task body before the next heading (e.g. `*Depends-on: 1,3*`). The CLI parses this line and writes `<depends-on>` into the XML automatically.
|
|
305
|
+
- Organise tasks as **thin vertical slices** in both modes.
|
|
306
|
+
8. **Final Review & Go-ahead:**
|
|
307
|
+
- `[Linear]` **Stop.** Post to the Linear project that the risk-sorted plan is ready for review.
|
|
308
|
+
- `[Local]` **Stop.** Tell the human the XML task file has been committed and the plan is ready for review.
|
|
309
|
+
- **Wait for explicit human go-ahead before proceeding to Phase 2.**
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Phase 2 — Execute
|
|
314
|
+
|
|
315
|
+
**Session-resume pre-flight `[Local]`** — If you are picking up a plan that was started in a previous session, run this before doing anything else:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan resume --plan {N}
|
|
319
|
+
# Prints: XML file present check and task state summary.
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Only proceed once you know which tasks are done and which are next.
|
|
323
|
+
|
|
324
|
+
**Where the plan files live `[Local]`** — Do not assume plan narratives are under
|
|
325
|
+
`.shipsmooth/plans/` in the project repo. In **external** mode (the default) the project
|
|
326
|
+
repo stays untouched and the plan files live in a separate state directory. Ask the CLI
|
|
327
|
+
where to read them — it is the source of truth — rather than guessing:
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store info --json
|
|
331
|
+
# -> {"status":"ready","mode":"external","stateRoot":"...","plansDir":"<dir>/plans"}
|
|
332
|
+
# Read plan narratives (plan-{N}.md) and task XML from the reported `plansDir`.
|
|
333
|
+
# If status is not "ready", state is not set up yet — handle per first-run (Phase 0).
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Load the plan narrative for `{N}` from the reported `plansDir` before executing, the same
|
|
337
|
+
as you would for an in-repo plan — `mode: in-repo` simply reports the in-repo `plansDir`.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
**Step 0: Create a branch**
|
|
342
|
+
|
|
343
|
+
Create a branch named after the primary issue for this plan:
|
|
344
|
+
```bash
|
|
345
|
+
${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan branch --issue {issue-id} --desc "{short-description}"
|
|
346
|
+
# prints: git push -u origin t/{issue-id}-{slug} — run that line to push
|
|
347
|
+
```
|
|
348
|
+
All task commits go on this branch. The `t/` prefix stands for "task". Usernames are omitted — the task identity is what matters long-term.
|
|
349
|
+
|
|
350
|
+
**Before writing any code**, confirm the test coverage threshold with the human (default: 95%). Record the agreed value before proceeding.
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
> **Commit-message convention (code commits in the project repo).** How you word a code
|
|
354
|
+
> commit depends on the resolved storage mode. Check it once per session with
|
|
355
|
+
> `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store info --json` and read `mode`.
|
|
356
|
+
>
|
|
357
|
+
> - **`in-repo` mode:** keep the prefixed convention — `task(N): <short description>` and
|
|
358
|
+
> `draft(N): de-risk <task name>`. The plan/task history is shipsmooth's own and lives
|
|
359
|
+
> alongside the code, so the prefixes are welcome.
|
|
360
|
+
> - **`external` (standalone) mode:** the project repo must stay **zero-trace**. Write
|
|
361
|
+
> plain, feature-oriented messages with **no `plan(N)`/`task(N)`/`draft(N)` prefix** and
|
|
362
|
+
> no plan or task references — e.g. `Add retry to upload client`, not
|
|
363
|
+
> `task(3): add retry`. This applies to **every** project-repo commit, including the
|
|
364
|
+
> preamble integration-test commit (write `Add end-to-end test for <feature>`, not a
|
|
365
|
+
> `plan(N)`-referencing message).
|
|
366
|
+
>
|
|
367
|
+
> Traceability is **not lost** in standalone mode: the task↔commit link lives in the state
|
|
368
|
+
> repo's task XML, recorded via `task set-commit`. State-repo commits (the plan file and
|
|
369
|
+
> task XML — the `plan(N): …` commit) keep full plan/task info; that history is shipsmooth's
|
|
370
|
+
> own and invisible to the user. This convention governs only the **project repo's** code
|
|
371
|
+
> commits.
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
### Preamble: integration tests (once, before any task)
|
|
375
|
+
|
|
376
|
+
1. Write 1–2 integration tests that exercise the feature end-to-end. No more than two.
|
|
377
|
+
2. Commit and push them with no implementation — they must fail (red). Word the commit message per the **commit-message convention** above (in standalone mode, no `plan(N)`/`task(N)` reference — this is a project-repo commit too). `[Linear]` Reference the Linear project in the commit message.
|
|
378
|
+
3. Confirm red state:
|
|
379
|
+
```bash
|
|
380
|
+
# run your project's test command, e.g.:
|
|
381
|
+
npm test # or: pytest, go test ./..., etc.
|
|
382
|
+
```
|
|
383
|
+
If a test passes at this point, it is testing the wrong thing. Fix or discard it before continuing.
|
|
384
|
+
|
|
385
|
+
### Per-task loop (The De-risk & Harden Cycle)
|
|
386
|
+
|
|
387
|
+
For every task in the risk-sorted sequence, apply the appropriate sub-phases:
|
|
388
|
+
|
|
389
|
+
#### High and Medium risk tasks — De-risk & Harden Cycle
|
|
390
|
+
|
|
391
|
+
##### Step A: De-risking (Spiral Phase)
|
|
392
|
+
- **Goal:** Validate logic and architectural direction. Ignore "Implementation Quality" rules.
|
|
393
|
+
- Write at least one failing test (and not more than 3) that targets the core logic (preserving
|
|
394
|
+
Core Invariant #6).
|
|
395
|
+
- Implement just enough to prove the approach works. Focus on the core complexity.
|
|
396
|
+
- Commit per the **commit-message convention**: `draft(N): de-risk [task name]` in in-repo mode; in standalone mode a plain feature message with no `draft(N)`/`task(N)` reference.
|
|
397
|
+
- `[Linear]` Post a comment on the Linear issue notifying the human the draft is ready.
|
|
398
|
+
- `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task status --plan {N} --task {id} --status de-risked` and `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task comment --plan {N} --task {id} --message "De-risk draft ready for review"`.
|
|
399
|
+
- **Wait for explicit approval of the approach.**
|
|
400
|
+
|
|
401
|
+
##### Step B: Hardening (Quality Phase)
|
|
402
|
+
- **Goal:** Achieve technical excellence, human readability, and coverage threshold.
|
|
403
|
+
- Refactor the de-risked code for readability, performance, and project patterns. If skill
|
|
404
|
+
"experimental-refine-dev" exists, then use it to improve the design.
|
|
405
|
+
- Follow Test Driven Development if possible: Write only one test at a time, then the implementing code
|
|
406
|
+
and then refactor.
|
|
407
|
+
- Keep doing Step B until coverage meets the agreed threshold (and if "experimental-refine-dev" skill exists,
|
|
408
|
+
quality conforms to its instructions):
|
|
409
|
+
```bash
|
|
410
|
+
# example — adjust to your toolchain:
|
|
411
|
+
npm test -- --coverage --coverageThreshold='{"global":{"lines":95}}'
|
|
412
|
+
```
|
|
413
|
+
- Commit the completed task (tests + implementation), wording the message per the **commit-message convention** (standalone → plain feature message, no `task(N)` prefix):
|
|
414
|
+
```bash
|
|
415
|
+
git commit -m "task(N): <short description>" # in-repo mode; standalone: plain feature message
|
|
416
|
+
git push origin t/{issue-id}-{short-description}
|
|
417
|
+
```
|
|
418
|
+
This creates a stable rollback point. A human reviewing the PR can check out this commit to inspect each task in isolation.
|
|
419
|
+
- `[Linear]` Mark the Linear issue **Agent Coded**.
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
- `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task status --plan {N} --task {id} --status agent-coded` and `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task set-commit --plan {N} --task {id} --commit $(git rev-parse HEAD)`.
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
#### Low risk tasks — Single-pass (current behavior)
|
|
426
|
+
|
|
427
|
+
1. Write the unit test(s) for this task. Commit them failing (red).
|
|
428
|
+
2. Implement the task. Run tests until green.
|
|
429
|
+
3. Check coverage meets the agreed threshold:
|
|
430
|
+
```bash
|
|
431
|
+
# example — adjust to your toolchain:
|
|
432
|
+
npm test -- --coverage --coverageThreshold='{"global":{"lines":95}}'
|
|
433
|
+
```
|
|
434
|
+
Do not proceed until coverage passes.
|
|
435
|
+
4. Commit the completed task (tests + implementation), wording the message per the **commit-message convention** (standalone → plain feature message, no `task(N)` prefix):
|
|
436
|
+
```bash
|
|
437
|
+
git commit -m "task(N): <short description>" # in-repo mode; standalone: plain feature message
|
|
438
|
+
git push origin t/{issue-id}-{short-description}
|
|
439
|
+
```
|
|
440
|
+
- `[Linear]` Mark the Linear issue **Agent Coded**. No draft review needed.
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
- `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task status --plan {N} --task {id} --status agent-coded` and `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task set-commit --plan {N} --task {id} --commit $(git rev-parse HEAD)`. No draft review needed.
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
- **Minor deviation** (task split, reorder, clarification):
|
|
449
|
+
- `[Linear]` Update the Linear issue(s), add a deviation comment explaining why, continue.
|
|
450
|
+
- `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task deviation --plan {N} --task {id} --type minor --message "..."`, continue.
|
|
451
|
+
- **Major deviation** (fundamental plan problem, architecture issue, blocked): Stop immediately.
|
|
452
|
+
- `[Linear]` Post a Linear project update. Set project health to **"At Risk"**.
|
|
453
|
+
- `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan update --plan {N} --blocked --message "..."`.
|
|
454
|
+
- Wait for the human to revise the plan file, commit, push, and give a new go-ahead.
|
|
455
|
+
|
|
456
|
+
Never autonomously modify the `.shipsmooth/plans/` file during execution. If a plan change is needed, surface it and wait.
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## Plan Closeout
|
|
461
|
+
|
|
462
|
+
### Clean Completion
|
|
463
|
+
```bash
|
|
464
|
+
git tag plan-07-complete
|
|
465
|
+
git push origin plan-07-complete
|
|
466
|
+
```
|
|
467
|
+
- `[Linear]` Close all Linear issues in the `[agent]` project. Mark `[agent]` project complete and archive it. Update the permanent backlog feature issue to reflect delivery (link to completing PR, note what was delivered).
|
|
468
|
+
- `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan update --plan {N} --status complete --message "Plan complete."`. Commit the final XML state. Update the permanent backlog feature issue (if tracked externally) or note delivery in the plan file.
|
|
469
|
+
|
|
470
|
+
### Completion with Loose Ends
|
|
471
|
+
- `[Linear]` Label unresolved issues `needs-triage`. Set `[agent]` project to **"In Review"**. Post a project update listing each open issue and why it's unresolved. Wait for human to review: they will promote worthy issues to the permanent backlog or discard them. Human marks the project complete and archives it.
|
|
472
|
+
- `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task status --plan {N} --task {id} --status needs-triage` for each unresolved task. Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan update --plan {N} --status in-review --message "..."`. Commit the XML. Wait for human to review.
|
|
473
|
+
|
|
474
|
+
### Abandonment
|
|
475
|
+
- Human commits a plan file deletion with a commit message referencing the superseding plan number
|
|
476
|
+
- You tag the deletion commit:
|
|
477
|
+
```bash
|
|
478
|
+
git tag plan-07-abandoned
|
|
479
|
+
git push origin plan-07-abandoned
|
|
480
|
+
```
|
|
481
|
+
- **Do not delete any earlier tags** (`plan-07-v1`, `plan-07-v2`, etc.) — they are the audit trail
|
|
482
|
+
- `[Linear]` Surface all open tasks for human triage. Migrate worthy tasks to the permanent backlog with a note: "Partial delivery — see plan-07-abandoned, superseded by plan-{M}". Archive the `[agent]` project with a closing note referencing the deletion commit hash and the superseding plan.
|
|
483
|
+
- `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan update --plan {N} --status abandoned --message "Superseded by plan-{M}."`. Commit the final XML state.
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Audit Trail
|
|
488
|
+
|
|
489
|
+
`[Linear]` Record in every Linear issue:
|
|
490
|
+
|
|
491
|
+
| Event | What to store in the issue |
|
|
492
|
+
|---|---|
|
|
493
|
+
| Task created | `github.com/.../blob/{plan-07-v1-hash}/.shipsmooth/plans/plan-07.md` |
|
|
494
|
+
| Task closed / obsoleted | `github.com/.../blob/{plan-07-vN-hash}/.shipsmooth/plans/plan-07.md` + one-line reason |
|
|
495
|
+
|
|
496
|
+
`[Local]` The XML file is the audit trail. `<created-from>` and `<closed-at-version>` child elements on each `<task>` serve the same role. The XML is versioned in git, so `git diff` between two plan tags shows exactly what changed.
|
|
497
|
+
|
|
498
|
+
If the creation version equals the closeout version, the plan never changed during execution. If they differ, the git diff between the two tag hashes shows exactly what changed and why.
|
|
499
|
+
|
|
500
|
+
Feature issues in the permanent backlog should accumulate references to every plan that contributed to them — this gives a full delivery history across the feature's lifetime.
|
|
501
|
+
|
|
502
|
+
---
|