@stdd/plugin 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +9 -0
- package/.codex-plugin/plugin.json +21 -0
- package/LICENSE +21 -0
- package/README.md +47 -0
- package/extensions/stdd.mjs +77 -0
- package/hooks/claude-hooks.json +28 -0
- package/hooks/codex-hooks.json +28 -0
- package/package.json +38 -0
- package/runtime/adapters/README.md +158 -0
- package/runtime/cli/check.mjs +555 -0
- package/runtime/cli/ci.mjs +190 -0
- package/runtime/cli/claude-hooks.mjs +689 -0
- package/runtime/cli/config.mjs +27 -0
- package/runtime/cli/evidence.mjs +249 -0
- package/runtime/cli/generated-files.mjs +1693 -0
- package/runtime/cli/held-fs.mjs +415 -0
- package/runtime/cli/init.mjs +883 -0
- package/runtime/cli/ledger.mjs +1470 -0
- package/runtime/cli/lib.mjs +909 -0
- package/runtime/cli/path-bytes.mjs +83 -0
- package/runtime/cli/policy.mjs +112 -0
- package/runtime/cli/recorders.mjs +188 -0
- package/runtime/cli/review-fs.mjs +825 -0
- package/runtime/cli/review.mjs +1065 -0
- package/runtime/cli/runtime.mjs +32 -0
- package/runtime/cli/scope.mjs +185 -0
- package/runtime/cli/snapshot.mjs +897 -0
- package/runtime/cli/state-validation.mjs +168 -0
- package/runtime/cli/status.mjs +580 -0
- package/runtime/cli/stdd.mjs +536 -0
- package/runtime/cli/worker-fs.mjs +971 -0
- package/runtime/cli/worker-metadata.mjs +139 -0
- package/runtime/cli/worker.mjs +779 -0
- package/runtime/method/README.md +634 -0
- package/runtime/method/reference-commands.md +147 -0
- package/runtime/method/reference-generated-state.md +151 -0
- package/runtime/method/reference-integration.md +233 -0
- package/runtime/package.json +65 -0
- package/runtime/playbooks/brainstorming.md +46 -0
- package/runtime/playbooks/debugging.md +36 -0
- package/runtime/playbooks/delegate-slice.md +129 -0
- package/runtime/playbooks/finish-change.md +46 -0
- package/runtime/playbooks/implement.md +26 -0
- package/runtime/playbooks/investigation.md +33 -0
- package/runtime/playbooks/managed-playbooks.json +14 -0
- package/runtime/playbooks/planning.md +177 -0
- package/runtime/playbooks/pr-green.md +50 -0
- package/runtime/playbooks/start-change.md +37 -0
- package/runtime/playbooks/worktrees.md +45 -0
- package/runtime/prebuilds/stdd-fs/darwin-arm64/stdd-fs +0 -0
- package/runtime/prebuilds/stdd-fs/darwin-x64/stdd-fs +0 -0
- package/runtime/prebuilds/stdd-fs/linux-arm64/stdd-fs +0 -0
- package/runtime/prebuilds/stdd-fs/linux-x64/stdd-fs +0 -0
- package/runtime/prebuilds/stdd-fs/manifest.json +47 -0
- package/runtime/prebuilds/stdd-fs/win32-arm64/stdd-fs.exe +0 -0
- package/runtime/prebuilds/stdd-fs/win32-x64/stdd-fs.exe +0 -0
- package/runtime/sdk/adapters.mjs +279 -0
- package/runtime/sdk/file-observation.mjs +12 -0
- package/runtime/sdk/index.d.ts +140 -0
- package/runtime/sdk/index.mjs +31 -0
- package/runtime/sdk/native-fs.mjs +1235 -0
- package/runtime/sdk/path.mjs +71 -0
- package/runtime/sdk/text.mjs +42 -0
- package/runtime/sdk/workflow.mjs +294 -0
- package/runtime/templates/deferred-design.md +47 -0
- package/runtime/templates/github-stdd.yml +42 -0
- package/runtime/templates/gitlab-stdd.yml +72 -0
- package/runtime/templates/pr-description.md +35 -0
- package/scripts/adopting-root.mjs +42 -0
- package/scripts/stdd-hook.mjs +72 -0
- package/skills/stdd-brainstorming/SKILL.md +48 -0
- package/skills/stdd-debugging/SKILL.md +38 -0
- package/skills/stdd-delegate-slice/SKILL.md +118 -0
- package/skills/stdd-finish-change/SKILL.md +40 -0
- package/skills/stdd-implement/SKILL.md +28 -0
- package/skills/stdd-investigation/SKILL.md +35 -0
- package/skills/stdd-planning/SKILL.md +165 -0
- package/skills/stdd-pr-green/SKILL.md +52 -0
- package/skills/stdd-start-change/SKILL.md +39 -0
- package/skills/stdd-worktrees/SKILL.md +46 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Reference: review and worker command internals
|
|
2
|
+
|
|
3
|
+
The mechanics behind `stdd review` and the worker commands: how a request is
|
|
4
|
+
stored and settled, what each dispatch route does, and what a managed sandbox
|
|
5
|
+
copies and collects. The method states the contract; this document states the
|
|
6
|
+
implementation.
|
|
7
|
+
|
|
8
|
+
## Brief construction
|
|
9
|
+
|
|
10
|
+
The brief carries a complete changed-file manifest (the diff body
|
|
11
|
+
may truncate beyond a size bound; the manifest never does, and it names
|
|
12
|
+
every untracked path too — symlinks and other non-regular files carry a
|
|
13
|
+
skipped marker, so nothing the reviewer was not told about can exist),
|
|
14
|
+
the diff, the contents of untracked regular files
|
|
15
|
+
(a new file is part of the change even before `git add`; symlinks are
|
|
16
|
+
skipped and large files are read only up to a bound), and a **governing
|
|
17
|
+
docs** section (the canonical docs are the standing spec: docs changed
|
|
18
|
+
in this branch are named as the spec delta to read first, and when none
|
|
19
|
+
changed the configured `canonicalDocs` globs are named instead — the
|
|
20
|
+
reviewer is read-only in the repository and reads them itself; contents
|
|
21
|
+
are never inlined).
|
|
22
|
+
|
|
23
|
+
## The review result contract
|
|
24
|
+
|
|
25
|
+
The reviewer's output is a single JSON object with required `summary` and
|
|
26
|
+
`findings` fields. `summary` and every finding's required `message` must be
|
|
27
|
+
non-empty printable single lines; ordinary Unicode, including ZWNJ/ZWJ and
|
|
28
|
+
emoji, remains valid. Each finding has `severity: blocking | advisory`,
|
|
29
|
+
`path` absent or null or a non-empty printable single line, and `line`
|
|
30
|
+
absent or null or a positive safe integer. An absent location field is
|
|
31
|
+
normalized to null for findings not tied to one location. For a
|
|
32
|
+
control-bearing repository path that cannot cross this inline boundary, the
|
|
33
|
+
reviewer omits `path` rather than emitting unsafe text. Any wrong field type
|
|
34
|
+
or output shape rejects the whole result.
|
|
35
|
+
|
|
36
|
+
## Brief storage and settlement
|
|
37
|
+
|
|
38
|
+
The brief is written outside the repository, in a
|
|
39
|
+
private temporary directory with owner-only permissions — it can carry
|
|
40
|
+
source contents and must not be world-readable. A `review-request` event
|
|
41
|
+
records the route, snapshot, brief hash, and a versioned, lossless identity
|
|
42
|
+
for the OS temp root, private directory, and every owned artifact. Codex's
|
|
43
|
+
`last-message.txt` is created owner-only before that event and read only
|
|
44
|
+
through a descriptor whose identity still matches the request. If the branch
|
|
45
|
+
or active task changes
|
|
46
|
+
while a CLI reviewer runs, the command records a terminal cancellation
|
|
47
|
+
against the captured original request rather than attaching a verdict to
|
|
48
|
+
the new context or leaving an orphan request. The cancellation and verdict
|
|
49
|
+
paths share the ledger lock, so exactly one terminal outcome wins.
|
|
50
|
+
|
|
51
|
+
Private-artifact settlement verifies the recorded directory and artifact
|
|
52
|
+
identities, including that every artifact's recorded and observed owner equals
|
|
53
|
+
the recorded review-directory owner, then overwrites each captured file through a helper-held writable
|
|
54
|
+
capability, flushes it, truncates it to zero, and flushes again. It then moves
|
|
55
|
+
the zeroed directory into an owner-private, non-loadable OS-temp quarantine.
|
|
56
|
+
After the terminal ledger outcome is durable, that identity-bound zeroed tree
|
|
57
|
+
remains for explicit operator removal. A crash leaves the quarantine
|
|
58
|
+
recoverable by `review --cleanup`; unknown siblings, changed identities, or
|
|
59
|
+
legacy requests without complete identity provenance fail closed before
|
|
60
|
+
mutation and require explicit operator remediation. Settlement never follows
|
|
61
|
+
or recursively deletes a replaceable final basename.
|
|
62
|
+
|
|
63
|
+
## Dispatch routes
|
|
64
|
+
|
|
65
|
+
- `--via codex` dispatches `codex exec --sandbox read-only` itself —
|
|
66
|
+
stdin closed, wall-clock bounded (`--timeout <seconds>`, default
|
|
67
|
+
600) — parses the reviewer's final message, and recomputes the
|
|
68
|
+
snapshot once the runner returns: a checkout that changed while the
|
|
69
|
+
reviewer ran records stale, the same as on submit.
|
|
70
|
+
- `--via claude` dispatches `claude -p --safe-mode --tools Read,Glob,Grep --permission-mode dontAsk`
|
|
71
|
+
headless in the same way — brief over stdin, bounded, and tool-enforced read-only — for
|
|
72
|
+
repositories driven from Codex, or as a second perspective; like codex it
|
|
73
|
+
requires the `crossCli` capability.
|
|
74
|
+
- `--via subagent` prints the brief path for the orchestrating agent to
|
|
75
|
+
hand to a fresh read-only subagent; the reviewer's JSON comes back via
|
|
76
|
+
`stdd review --result <file|->`, which grades it against the **open
|
|
77
|
+
subagent request**: a snapshot mismatch with the current checkout
|
|
78
|
+
records the result as stale and rejects it, and a CLI-dispatched
|
|
79
|
+
request (codex or claude) can never be completed by `--result` — its
|
|
80
|
+
runner is its only mouth, so a hand-fed file cannot forge its provenance.
|
|
81
|
+
Submitting a result securely settles the private temporary artifacts. An
|
|
82
|
+
abandoned request is cancelled and settled with `stdd review --cleanup`;
|
|
83
|
+
cleanup also reaches an interrupted CLI request and retries settlement
|
|
84
|
+
when a terminal cancellation outlived its private-artifact move.
|
|
85
|
+
|
|
86
|
+
## Declaring a scope
|
|
87
|
+
|
|
88
|
+
`stdd slice new` declares a scope inside the existing checkout;
|
|
89
|
+
`stdd worker create <directory>` declares the same scope and builds a managed
|
|
90
|
+
filesystem snapshot without `.git`. Both accept `--frozen` (globs the slice
|
|
91
|
+
must not touch) and `--allowed` (globs the slice may touch — anything outside
|
|
92
|
+
is a violation), and both record a `scope` event carrying the globs and a
|
|
93
|
+
baseline. Every glob crosses the same printable-single-line
|
|
94
|
+
boundary as other persisted identifiers; control, bidi, and invisible
|
|
95
|
+
formatting characters are rejected before durable state is written.
|
|
96
|
+
|
|
97
|
+
## Managed worker sandboxes
|
|
98
|
+
|
|
99
|
+
A managed worker sandbox created by `stdd worker create` requires an active
|
|
100
|
+
task and an already recorded docs
|
|
101
|
+
decision. Its destination must not exist. Managed create and collect use the
|
|
102
|
+
native mutation helper and fail before mutation when the destination
|
|
103
|
+
filesystem cannot provide the required capability guarantees. Creation copies
|
|
104
|
+
the checkout's tracked and non-ignored untracked files at
|
|
105
|
+
current bytes, excluding Git metadata and the private ledger/plan. Ignored dependencies, credentials, and
|
|
106
|
+
build output are deliberately absent; run the repository's readiness setup in
|
|
107
|
+
the sandbox before trusting tests. `.stdd/worker.json` binds the sandbox ID,
|
|
108
|
+
source task and branch, scope, source HEAD, and every copied path fingerprint;
|
|
109
|
+
the parent ledger records the metadata hash. The sandbox receives a minimal
|
|
110
|
+
local ledger for the same task, so `status --local`, `red`, `verify`, `note`,
|
|
111
|
+
`scope`, and `doctor --readiness` work without source Git authority. Task boundaries, docs decisions, nested worker
|
|
112
|
+
creation, review, evidence, and delivery commands are rejected there.
|
|
113
|
+
|
|
114
|
+
`stdd worker collect <directory>` runs only from the source checkout. It
|
|
115
|
+
rejects a missing or changed metadata binding, any non-ignored `.git` entry,
|
|
116
|
+
unsafe file type, scope violation, source task/branch/HEAD drift, or concurrent source
|
|
117
|
+
edit to a worker-touched path before applying anything. A complete preflight
|
|
118
|
+
then imports only worker-introduced file changes and the worker's red/verify/
|
|
119
|
+
note evidence. Collection never stages, commits, switches branches, pushes, or
|
|
120
|
+
otherwise changes Git history. Import is idempotent: a rerun accepts paths
|
|
121
|
+
already at the sandbox result and completes any remaining paths after an
|
|
122
|
+
interruption; any third state is a conflict. Deleted source bytes move into an
|
|
123
|
+
owner-private, Git-ignored `.stdd/worker-deletions/` recovery quarantine.
|
|
124
|
+
Interrupted collection reuses exact deletion baselines for the next idempotent
|
|
125
|
+
run; completed baselines remain recognizable for explicit operator removal.
|
|
126
|
+
A readable metadata-v1 sandbox may replace file content while retaining its
|
|
127
|
+
exact validated baseline mode, including a legacy mode such as `0664`; this
|
|
128
|
+
compatibility authority never permits a sandbox-selected mode change and does
|
|
129
|
+
not relax metadata-v2 creation modes. The orchestrator still runs fresh
|
|
130
|
+
verification and review in the source checkout. STDD never removes the
|
|
131
|
+
sandbox automatically; the orchestrator deletes it explicitly only after
|
|
132
|
+
reviewing the collected result.
|
|
133
|
+
|
|
134
|
+
## The scope postflight
|
|
135
|
+
|
|
136
|
+
`stdd scope` is the postflight check against the recorded baseline rather than
|
|
137
|
+
a ref: Git checkouts compare HEAD plus dirty paths, while managed sandboxes
|
|
138
|
+
compare their bound file manifest. Only **worker-introduced** changes count — a
|
|
139
|
+
change to a frozen path, or outside the allowed paths, fails. Dirt inherited
|
|
140
|
+
from before the slice (a file already modified at baseline, byte-identical
|
|
141
|
+
now) is reported separately and never blamed on the slice. A declared slice
|
|
142
|
+
exempts only the ledger, plan, and exact shape-validated private internal
|
|
143
|
+
transaction names;
|
|
144
|
+
tracked config, generated files, and reset-name near misses under `.stdd/`
|
|
145
|
+
remain ordinary scope inputs. The same exact exemption boundary applies to
|
|
146
|
+
checkout and review snapshots. A declared slice appears in `stdd status`,
|
|
147
|
+
which names the postflight as the next step once the loop is complete.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Reference: generated state and durable mutation
|
|
2
|
+
|
|
3
|
+
How STDD proves that its generated files are the ones it wrote, and how it
|
|
4
|
+
mutates a namespace without losing state to a crash. The method summarizes the
|
|
5
|
+
guarantee; this document holds the mechanism.
|
|
6
|
+
|
|
7
|
+
## Manifest hashes and generated-file cleanup
|
|
8
|
+
|
|
9
|
+
Manifest hashes prove that generated bytes have not changed since init; for
|
|
10
|
+
the installed method, `check` and `doctor` also compare those bytes with the
|
|
11
|
+
canonical method shipped by the running CLI, so a stale method and a matching
|
|
12
|
+
stale manifest cannot authenticate each other. Generated-looking method,
|
|
13
|
+
snippet, and native-skill files without a manifest are a partial install and
|
|
14
|
+
also fail the check, even when an exact generated path is occupied by a
|
|
15
|
+
directory, symlink, or other unsafe object. A malformed manifest — including a wrong generator or
|
|
16
|
+
version shape, a non-object document, an invalid generated path or exact
|
|
17
|
+
`sha256:<64 lowercase hex>` file hash, or malformed present target metadata —
|
|
18
|
+
is an actionable adoption finding in both `check` and `doctor`, never an
|
|
19
|
+
uncaught runtime error. Legacy manifests may omit target metadata; when
|
|
20
|
+
present, it must contain the complete known target schema. Every listed
|
|
21
|
+
output is inspected as a readable regular file without following symlinks;
|
|
22
|
+
directories, non-regular files, unreadable paths, and paths replaced during
|
|
23
|
+
inspection are stale findings rather than runtime errors. Profile cleanup
|
|
24
|
+
uses the same inspection boundary and never deletes an output it cannot
|
|
25
|
+
safely identify. Retirement is confined to a filesystem capability held by
|
|
26
|
+
the native mutation helper: the verified inode is moved out of its agent or
|
|
27
|
+
CI load path to an unpredictable quarantine name. That quarantine is a
|
|
28
|
+
durable safety and crash-recovery boundary; STDD never turns a final basename
|
|
29
|
+
check into automatic recursive deletion. Before the first quarantine rename,
|
|
30
|
+
init atomically publishes and fsyncs `.stdd/cleanup-transaction.json`
|
|
31
|
+
with each output's original and quarantine paths, expected hash, and captured
|
|
32
|
+
parent and file identities. Every later phase is durably recorded. A
|
|
33
|
+
manifest-publish failure rolls back all quarantines; after a durable manifest
|
|
34
|
+
commit the exact quarantined trees remain hash-accounted outside their former
|
|
35
|
+
load paths for explicit operator removal. A crash leaves the journal
|
|
36
|
+
authoritative, and the next init reconstructs whether to resume or roll back.
|
|
37
|
+
If recovery cannot prove the exact identities, it blocks without forgetting
|
|
38
|
+
an orphan. `check` and `doctor` report pending or malformed cleanup journals;
|
|
39
|
+
`doctor` also inventories recognized retained quarantines and gives manual
|
|
40
|
+
removal guidance. Neither treats a quarantine as active generated output. A hand-edited formerly generated file is preserved and
|
|
41
|
+
remains listed and reported stale until its owner removes or relocates it.
|
|
42
|
+
The journal is repo-local, single-linked, owner-private, and replaced
|
|
43
|
+
atomically. Journal reads bind metadata, bytes, and the final pathname to one
|
|
44
|
+
helper-held file capability. Recovery rollback and retirement use captured
|
|
45
|
+
parent capabilities, so a concurrent logical-parent swap cannot redirect
|
|
46
|
+
either operation. The replacement manifest is committed in durable order:
|
|
47
|
+
held-parent temp creation, file flush, held-parent atomic rename, directory
|
|
48
|
+
flush, then logical-parent and manifest-identity verification. Only after
|
|
49
|
+
that proof may the zeroed journal itself move to a retained, non-loadable
|
|
50
|
+
quarantine. An indeterminate post-rename failure leaves all state for
|
|
51
|
+
deterministic recovery.
|
|
52
|
+
|
|
53
|
+
## The bundled `stdd-fs` helper
|
|
54
|
+
|
|
55
|
+
Namespace mutations that require a stable parent run through one bundled
|
|
56
|
+
`stdd-fs` helper session per CLI command. The helper exposes a versioned JSONL
|
|
57
|
+
capability protocol; opaque handles and expected identity tokens, not logical
|
|
58
|
+
pathnames, authorize mutation. STDD's JavaScript runtime retains policy, WAL
|
|
59
|
+
schemas, operation ordering, and user-facing diagnostics. The helper binaries
|
|
60
|
+
for Linux x64/arm64, macOS x64/arm64, and Windows x64/arm64 are included in
|
|
61
|
+
both `@stdd/cli` and the universal plugin. Release CI builds every target and
|
|
62
|
+
verifies the packaged artifact manifest. Before spawning one, STDD verifies
|
|
63
|
+
the exact platform/architecture selection, regular non-symlinked file shape,
|
|
64
|
+
SHA-256, and protocol handshake. The installed package tree is part of the
|
|
65
|
+
trusted code boundary, like the JavaScript runtime itself; integrity checks
|
|
66
|
+
detect static corruption but do not claim to defeat a same-user process that
|
|
67
|
+
can rewrite executing package code. Target-repository namespace races remain
|
|
68
|
+
untrusted after the helper session starts. A missing, damaged, or incompatible
|
|
69
|
+
helper fails before target mutation; read-only commands do not require it except
|
|
70
|
+
when `doctor` capability-inspects exact ledger-proven retained review or worker
|
|
71
|
+
quarantine locations. Those inspections use only read operations through the
|
|
72
|
+
verified helper session; discovery or helper failures become doctor findings.
|
|
73
|
+
|
|
74
|
+
The helper preflights the target filesystem metadata and OS primitive set for stable file identities,
|
|
75
|
+
no-follow traversal, same-volume atomic rename, and durable file and directory
|
|
76
|
+
flushes. Unix binds owner and mode through descriptor-relative operations.
|
|
77
|
+
Windows binds volume/file IDs, rejects reparse points, and creates private
|
|
78
|
+
state with a protected current-user DACL. Symbolic-link inspection is a
|
|
79
|
+
protocol-v1, capability-relative `read-link` operation over a held parent and
|
|
80
|
+
one validated child name: it never follows the link, requires the caller's
|
|
81
|
+
expected symlink identity, returns bounded raw target bytes as base64, and
|
|
82
|
+
rejects an identity change across the read. Unix uses bounded `readlinkat`
|
|
83
|
+
retries with an identity postflight. Windows accepts only recognized symbolic
|
|
84
|
+
link reparse tags and binds the exact reparse identity before and after parsing.
|
|
85
|
+
Symbolic-link creation uses the same held-parent authority and no-replace
|
|
86
|
+
publication. On Windows it selects file or directory link semantics without
|
|
87
|
+
following an external target, applies the exact owner and protected DACL,
|
|
88
|
+
verifies the published reparse identity, and flushes and postflights the
|
|
89
|
+
parent. Missing privilege or developer-mode support fails with a structured
|
|
90
|
+
unsupported or access error before partial publication. Probe evidence covers
|
|
91
|
+
both link operations consistently. Managed-worker creation performs that
|
|
92
|
+
preflight against the held, probed destination parent after preparing the
|
|
93
|
+
complete source snapshot and before creating the destination root whenever the
|
|
94
|
+
snapshot contains a symbolic link; regular-only snapshots skip it.
|
|
95
|
+
Protocol-v1 also exposes an expected-identity-bound `set-mode` mutation for a
|
|
96
|
+
held regular file on Unix. It uses descriptor-relative `fchmod`, rejects type
|
|
97
|
+
or special bits outside the legacy metadata-v1 mode range, and returns a stat
|
|
98
|
+
postflight with committed mutation reporting after the syscall. Windows
|
|
99
|
+
returns a structured unsupported result because managed metadata-v1 workers
|
|
100
|
+
are Linux-only. Collection may use this operation only to restore the exact
|
|
101
|
+
mode inherited from the validated metadata-v1 baseline for that file; a worker
|
|
102
|
+
cannot select a different mode. Metadata-v2 and new file creation remain
|
|
103
|
+
restricted to `0600`, `0644`, and `0755`. A filesystem without those
|
|
104
|
+
capabilities fails closed; there is no best-effort pathname fallback. New
|
|
105
|
+
quarantines always record enough provenance for deterministic recovery and
|
|
106
|
+
operator inventory. Existing manifest, journal, worker, review, ledger, and
|
|
107
|
+
plugin quarantines are never discovered by a broad temp-directory scan;
|
|
108
|
+
recognized locations are reported, while provenance-less leftovers remain
|
|
109
|
+
untouched.
|
|
110
|
+
|
|
111
|
+
## The printable-text boundary
|
|
112
|
+
|
|
113
|
+
Inline identifiers and adapter metadata share one printable-text boundary
|
|
114
|
+
before they reach task state, logs, or generated files. It accepts ordinary
|
|
115
|
+
Unicode, including ZWNJ/ZWJ and emoji sequences, but rejects line/control
|
|
116
|
+
characters, unpaired surrogate code units, Unicode `Bidi_Control` code points,
|
|
117
|
+
and a fixed denylist of invisible or deprecated formatting controls. The
|
|
118
|
+
denylist includes soft-hyphen/grapheme-joiner controls, zero-width space,
|
|
119
|
+
word/invisible-operator controls, interlinear/shorthand/music formatting
|
|
120
|
+
controls, and BOM, so visually identical or reordered text cannot spoof those
|
|
121
|
+
outputs without broadly rejecting Unicode format characters.
|
|
122
|
+
|
|
123
|
+
## Ledger transactions and retained quarantines
|
|
124
|
+
|
|
125
|
+
Reset publishes its two task
|
|
126
|
+
boundaries with one same-directory atomic rename. Its exact internal
|
|
127
|
+
active transaction names (`.ledger-reset-` and `.ledger-prepared-`, each
|
|
128
|
+
followed by 32 lowercase hex characters and `.tmp`) are owner-only. A trusted
|
|
129
|
+
stranded active temp moves under the next ledger lock to
|
|
130
|
+
`.stdd/ledger-quarantines/.ledger-recovered-<same-token>.tmp/`, an owner-private
|
|
131
|
+
retained directory containing a single-linked `0600` payload and matching
|
|
132
|
+
`0600` `inventory.json`. Active temps and those two retained files are ignored
|
|
133
|
+
by checkout/review snapshots only after their complete shape, token-bound
|
|
134
|
+
provenance, and exact directory contents are verified. Unix recognition also
|
|
135
|
+
binds uid and exact modes; Windows relies on the protected current-user DACL
|
|
136
|
+
and owner check enforced by the native creator because Node exposes only
|
|
137
|
+
synthetic POSIX uid/mode values there. They are deliberately not hidden by
|
|
138
|
+
`.gitignore`, so a symlink, non-regular or hard-linked file, extra sibling,
|
|
139
|
+
malformed inventory, near-miss name, or (on Unix) foreign owner/non-private
|
|
140
|
+
mode remains visible and is rejected rather than trusted. `stdd doctor` inventories verified retained ledger quarantines with
|
|
141
|
+
manual-removal guidance. Repository transaction-temp recovery, reset commit,
|
|
142
|
+
and settlement use
|
|
143
|
+
one native-helper session with held `.stdd` capabilities for the snapshot,
|
|
144
|
+
active and prepared temps, final ledger publication, and identity-conditioned
|
|
145
|
+
quarantine retirement. A crash leaves the exact temp recoverable; a completed
|
|
146
|
+
transaction moves it out of every active transaction namespace but does not
|
|
147
|
+
recursively delete its final quarantine basename. Ordinary task start, finish, and
|
|
148
|
+
recorder appends use the same portable publication boundary when they need a
|
|
149
|
+
namespace mutation. A SIGKILL-stranded active temp is a non-authoritative copy
|
|
150
|
+
recovered under the next ledger lock only after its owner-only, single-link
|
|
151
|
+
regular-file shape is validated, which is why a review snapshot may exempt it.
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Reference: installation, agent hosts, and automation
|
|
2
|
+
|
|
3
|
+
What `stdd init` and `stdd configure` write, what each agent host receives,
|
|
4
|
+
and how the optional CI and lifecycle-hook adapters behave. The method states
|
|
5
|
+
that these surfaces exist; this document states what they do.
|
|
6
|
+
|
|
7
|
+
## Choosing the profile: `init` and `configure`
|
|
8
|
+
|
|
9
|
+
`stdd init
|
|
10
|
+
--capabilities <list>` writes the profile without hand-editing JSON
|
|
11
|
+
(named capabilities on, the rest off), and `stdd init --interview` asks
|
|
12
|
+
one question at a time — recommended answer first — then runs the same
|
|
13
|
+
init. The interview also picks the reviewer route (`review.via`) and,
|
|
14
|
+
for the selected native agents (Claude Code, Codex, and Pi), offers the
|
|
15
|
+
lifecycle integration.
|
|
16
|
+
When `crossCli` is selected, the first selected native host is the driver for
|
|
17
|
+
the repository-level reviewer default and its opposite CLI is recorded:
|
|
18
|
+
Claude → codex, Codex → claude, Pi → claude. Per-host generated skills use the
|
|
19
|
+
same opposite-host rule explicitly, so a repository compiling multiple hosts
|
|
20
|
+
never teaches Claude Code or Codex to review itself; Pi is a driver host, not a
|
|
21
|
+
`stdd review --via` runner. With no dispatch capability, generated skills omit
|
|
22
|
+
the `[review:]` claim and review commands entirely; manual self-review is never
|
|
23
|
+
presented as an independent-review fallback.
|
|
24
|
+
|
|
25
|
+
`stdd configure` re-runs the interview over an existing install, with
|
|
26
|
+
the **current** values as the defaults. It edits only the capability
|
|
27
|
+
profile and the review route — every other config key is preserved —
|
|
28
|
+
and recompiles the same generated targets the last init produced (the
|
|
29
|
+
manifest remembers them). It does not install or remove CI workflows,
|
|
30
|
+
change CI target selection, or remove lifecycle hooks. Stop hooks are the
|
|
31
|
+
explicit exception: a remembered Stop-hook target is maintained (and restored
|
|
32
|
+
if missing), while `--stop-hook` opts in and may install it for the selected
|
|
33
|
+
agents. Configure never adds pre-push or session hooks. Flag forms skip the
|
|
34
|
+
questions: `--capabilities <list>`,
|
|
35
|
+
`--review-via subagent|codex|claude`, `--max-rounds <n>` (the review
|
|
36
|
+
budget; 0 = unlimited), `--stop-hook`. When the profile has a dispatch
|
|
37
|
+
capability, an incompatible route (codex or claude without `crossCli`,
|
|
38
|
+
subagent without `subagents`) is an error, never a silent downgrade to
|
|
39
|
+
self-review. A profile with no dispatch capability may retain a dormant
|
|
40
|
+
route because it emits no review claim; invoking or claiming review still
|
|
41
|
+
requires an available route.
|
|
42
|
+
|
|
43
|
+
## Agent host outputs
|
|
44
|
+
|
|
45
|
+
For Codex (`--tools codex`), init compiles every active playbook to
|
|
46
|
+
`.agents/skills/<name>/SKILL.md`, where Codex can select it implicitly from
|
|
47
|
+
its description or the user can invoke it explicitly as `$<name>`. Init also
|
|
48
|
+
maintains the repo's `AGENTS.md`: the short STDD section is written between
|
|
49
|
+
`stdd:begin`/`stdd:end` marker comments. The file is created when absent, the
|
|
50
|
+
marked section is replaced in place when present, and content outside the
|
|
51
|
+
markers is never touched. The section is also saved to
|
|
52
|
+
`.stdd/AGENTS-snippet.md` for manual composition.
|
|
53
|
+
|
|
54
|
+
For Claude Code (`--tools claude`), the same playbooks compile to
|
|
55
|
+
`.claude/skills/<name>/SKILL.md`, invocable as `/<name>` or selected
|
|
56
|
+
implicitly. Init maintains the same short invariant block in `CLAUDE.md`,
|
|
57
|
+
between the same managed markers, and saves it as
|
|
58
|
+
`.stdd/CLAUDE-snippet.md`. `AGENTS.md` and `CLAUDE.md` stay user-owned and
|
|
59
|
+
are never manifest-tracked; the generated snippets and native skills are
|
|
60
|
+
manifest-tracked. The full method is never injected into every prompt:
|
|
61
|
+
always-on files point to `.stdd/method.md`, while skills load their detailed
|
|
62
|
+
workflow only when used.
|
|
63
|
+
|
|
64
|
+
For Pi (`--tools pi`), init uses the Agent Skills standard registry at
|
|
65
|
+
`.agents/skills/<name>/SKILL.md`, which Pi discovers natively and invokes as
|
|
66
|
+
`/skill:<name>`. That output is byte-identical to Codex's skill files, so a
|
|
67
|
+
repository selecting both hosts has one shared generated copy rather than
|
|
68
|
+
duplicate skill names. Pi's short router lives in `.pi/APPEND_SYSTEM.md`, not
|
|
69
|
+
`AGENTS.md`: this keeps Pi's `/skill:` syntax from overwriting Codex's `$`
|
|
70
|
+
syntax when both hosts are selected. The append-system file is user-owned;
|
|
71
|
+
init maintains only its marked STDD section and saves the generated source as
|
|
72
|
+
`.stdd/PI-snippet.md`.
|
|
73
|
+
|
|
74
|
+
## Adoption modes and the universal bundle
|
|
75
|
+
|
|
76
|
+
STDD has three cumulative adoption modes. **Personal plugin** use installs the
|
|
77
|
+
universal STDD bundle once through Codex, Claude Code, or Pi and changes no
|
|
78
|
+
repository; its lazy skills remain available, while lifecycle integrations stay
|
|
79
|
+
dormant outside a checkout containing `.stdd/`.
|
|
80
|
+
**Shared repository contract** use runs `init` once and commits `.stdd/`, native
|
|
81
|
+
agent routing, and repository policy. **Enforced contract** use explicitly adds
|
|
82
|
+
repository-owned hooks or a CI adapter; ordinary `init` never creates CI, and
|
|
83
|
+
CI reads checkout and review-request facts rather than the private ledger or
|
|
84
|
+
agent state.
|
|
85
|
+
|
|
86
|
+
Repo-local generated skills remain a valid team contract and need no plugin.
|
|
87
|
+
The optional universal bundle at `plugins/stdd/` distributes one generated set
|
|
88
|
+
of conservative-profile skills and one CLI runtime through native Codex and
|
|
89
|
+
Claude Code plugin manifests or the `@stdd/plugin` Pi package. The runtime is
|
|
90
|
+
generated from the same source and version as `@stdd/cli`; the adopting
|
|
91
|
+
repository does not install that CLI package. The bundle never owns repository
|
|
92
|
+
state: its lifecycle integration acts only when the checkout contains
|
|
93
|
+
`.stdd/`; init, task state, policy, and optional CI stay with the repository.
|
|
94
|
+
|
|
95
|
+
Codex and Claude Code use the bundle's fail-open SessionStart and Stop command
|
|
96
|
+
hooks. If the bundled runtime cannot read an adopting checkout, SessionStart
|
|
97
|
+
reports fixed update-or-reinitialize guidance and exits successfully; Stop
|
|
98
|
+
returns the host's allow response without forwarding runtime output. Pi loads
|
|
99
|
+
the same skills plus a package extension. On `session_start` and
|
|
100
|
+
`session_compact`, that extension queues successful local status output for
|
|
101
|
+
the next model turn; on `agent_settled`, it queues at most one corrective
|
|
102
|
+
follow-up when the gate blocks. Runtime errors remain fail-open and are never
|
|
103
|
+
sent into a model turn. The installed bundle version governs all three hosts'
|
|
104
|
+
lifecycle commands.
|
|
105
|
+
|
|
106
|
+
Repository-generated pre-push/session/stop hooks are a separate integration and continue to require the exact project-local package
|
|
107
|
+
for pinned offline execution. The source-checkout command
|
|
108
|
+
`npm run build:plugin` validates every host manifest and publishes the shared
|
|
109
|
+
skills, Pi extension, runtime, and all six native mutation helpers through the
|
|
110
|
+
same capability boundary as the CLI. Retired stale skills move to an
|
|
111
|
+
identity-bound, non-loadable quarantine that remains available for explicit
|
|
112
|
+
operator removal; subsequent builds keep recognized quarantines stable.
|
|
113
|
+
|
|
114
|
+
## Project-local recipes
|
|
115
|
+
|
|
116
|
+
Project-specific recipes live in `.stdd/playbooks/local/` — markdown
|
|
117
|
+
playbooks with the same frontmatter contract (`name`, `description`,
|
|
118
|
+
`when`, optional `requires`), owned by the repository and never
|
|
119
|
+
overwritten by `stdd init`. They compile through the same pipeline as
|
|
120
|
+
the kit's playbooks — capability blocks included — into each selected host's
|
|
121
|
+
native skill registry. Always-on AGENTS/CLAUDE blocks remain a fixed, minimal
|
|
122
|
+
router: they name the method, the project-local runner, and `.stdd/policy.md`,
|
|
123
|
+
and do not enumerate either kit or project skills. A local recipe that
|
|
124
|
+
reuses a kit playbook's `name` replaces it: project knowledge outranks the kit.
|
|
125
|
+
Local recipe names must otherwise be unique; init rejects duplicates before
|
|
126
|
+
writing generated state and names both conflicting source files.
|
|
127
|
+
The three skills named by that router (`stdd-start-change`, `stdd-implement`,
|
|
128
|
+
and `stdd-finish-change`) are mandatory; init rejects a profile or local
|
|
129
|
+
override that would make one inactive. Other inactive local overrides still
|
|
130
|
+
shadow their kit playbook intentionally.
|
|
131
|
+
|
|
132
|
+
## CI adapters
|
|
133
|
+
|
|
134
|
+
CI integration is an explicit, optional transport adapter around
|
|
135
|
+
provider-neutral CLI contracts. `init` without `--ci` creates no provider file;
|
|
136
|
+
a team may instead place the printed generic commands in an existing quality
|
|
137
|
+
job. Every configured provider runs `stdd check`; a review pipeline pipes its
|
|
138
|
+
live PR/MR description to `stdd check-pr - --base <ref>`. CI uses read-only
|
|
139
|
+
repository and review-request access. It never attempts to prove the agent's
|
|
140
|
+
reasoning, consume the ignored ledger, dispatch workers, or mutate Git: it
|
|
141
|
+
grades only facts derivable from the checkout and review request.
|
|
142
|
+
|
|
143
|
+
On GitHub, `stdd init --ci github` writes the canonical workflow for these
|
|
144
|
+
gates and installs an explicit supported Node runtime. It fetches the PR body
|
|
145
|
+
live from the API and re-runs on body edits —
|
|
146
|
+
a workflow reading `github.event.pull_request.body` validates a payload
|
|
147
|
+
frozen at trigger time, so an edited body is never re-checked and a re-run
|
|
148
|
+
replays the stale text. The fetch uses node, not the gh CLI — node is
|
|
149
|
+
already required to run stdd, while self-hosted runners often lack gh —
|
|
150
|
+
and the step sets `pipefail`, so a failed fetch fails the gate as a fetch
|
|
151
|
+
error instead of feeding check-pr an empty body that misreports as a
|
|
152
|
+
missing evidence line. `stdd doctor` flags the frozen-payload form, and flags a PR
|
|
153
|
+
template carrying an unquoted evidence label at the start of a line, since
|
|
154
|
+
its placeholder residue would pass the gate on every PR.
|
|
155
|
+
|
|
156
|
+
On GitLab, `stdd init --ci gitlab` writes an includeable
|
|
157
|
+
`.gitlab/stdd.gitlab-ci.yml` job. It uses the merge-request API to fetch the
|
|
158
|
+
live description, pipes it to `check-pr -`, and passes
|
|
159
|
+
`CI_MERGE_REQUEST_DIFF_BASE_SHA` as the base. The job enables `pipefail`, so
|
|
160
|
+
an API failure fails the gate instead of being mistaken for an empty body.
|
|
161
|
+
Same-project pipelines authenticate with the short-lived `CI_JOB_TOKEN`.
|
|
162
|
+
Because fork merge-request pipelines normally run in the source project, the
|
|
163
|
+
target must allowlist that source for job-token access. A controlled trusted
|
|
164
|
+
fork may instead supply a masked and hidden target-project
|
|
165
|
+
`STDD_GITLAB_READ_API_TOKEN` with only `read_api`; target credentials are
|
|
166
|
+
never safe in an untrusted fork pipeline. Authentication failure names the
|
|
167
|
+
required setup instead of pretending fork access is automatic.
|
|
168
|
+
`stdd init --ci generic` writes no provider file; it prints and records the
|
|
169
|
+
portable command contract for teams to compose into Jenkins, Buildkite, or an
|
|
170
|
+
existing pipeline. Provider templates are adapters, never dependencies of
|
|
171
|
+
the method or public SDK.
|
|
172
|
+
|
|
173
|
+
## Local hooks
|
|
174
|
+
|
|
175
|
+
Locally, `stdd init --hooks` writes a pre-push hook that runs exactly one
|
|
176
|
+
fast, offline command: `stdd check`. Nothing network-bound belongs in a
|
|
177
|
+
hook — a flaky gate's false positives train `--no-verify`. The hook file
|
|
178
|
+
is user-owned after generation (like `config.json`, it is not
|
|
179
|
+
manifest-tracked and never overwritten), so teams append their own steps.
|
|
180
|
+
stdd never touches `.git/`: install it via
|
|
181
|
+
`git config core.hooksPath .stdd/hooks`, or call `stdd check` from an
|
|
182
|
+
existing hook manager. `stdd doctor` reports whether the hook is wired
|
|
183
|
+
up — informationally, never as a failure.
|
|
184
|
+
|
|
185
|
+
Generated hooks invoke the project-local package offline and name the scoped
|
|
186
|
+
package explicitly:
|
|
187
|
+
`npm exec --offline --package=@stdd/cli@<generated-version> -- stdd`. They
|
|
188
|
+
never ask npm to resolve the unrelated unscoped package `stdd`. Install
|
|
189
|
+
`@stdd/cli` as an exact development dependency before wiring hooks. The
|
|
190
|
+
`@stdd/cli` source repository is the one dogfood exception: its generated
|
|
191
|
+
automation invokes `node "$(git rev-parse --show-toplevel)/cli/stdd.mjs"`
|
|
192
|
+
directly, because the checkout being tested is the package source and may not
|
|
193
|
+
exist in npm's offline cache yet.
|
|
194
|
+
|
|
195
|
+
For selected native agents, `stdd init --session-hook` wires the session-start
|
|
196
|
+
ritual mechanically. Claude Code and Codex each get one `SessionStart` hook
|
|
197
|
+
(`startup|resume|clear|compact`) in their native settings. Pi gets a
|
|
198
|
+
project-local `.pi/extensions/stdd.js` extension that runs the same command on
|
|
199
|
+
`session_start` and `session_compact`, then queues its output for the next
|
|
200
|
+
model turn. The `compact` source is the single context-restoration path;
|
|
201
|
+
re-init removes older managed Claude `PostCompact` entries to avoid running the
|
|
202
|
+
ritual twice, while preserving unrelated user hooks. Each integration runs
|
|
203
|
+
`stdd status --local`, which never calls a forge or the network, so every fresh
|
|
204
|
+
context opens with local loop state and the next step already in it — recorded
|
|
205
|
+
state instead of recall. Hook entries are merged into existing valid files
|
|
206
|
+
without duplication. A conflicting Pi extension or invalid JSON settings are
|
|
207
|
+
left untouched and a manual instruction is printed instead. Codex hooks and
|
|
208
|
+
Pi project extensions remain subject to their host's repository trust review.
|
|
209
|
+
|
|
210
|
+
`stdd init --stop-hook` (opt-in, also offered by the interview and
|
|
211
|
+
`stdd configure`) wires the other end of the selected native agents. Claude
|
|
212
|
+
Code and Codex receive a `Stop` hook running the agent-specific
|
|
213
|
+
`stdd stop-hook` protocol, which applies the same judgment as `status --gate`
|
|
214
|
+
when the agent tries to finish. Pi has no pre-stop veto event; its project
|
|
215
|
+
extension checks the same gate at `agent_settled` and queues at most one
|
|
216
|
+
corrective follow-up turn when blocked. It then fails open rather than creating
|
|
217
|
+
an unbounded feedback loop, so this is visible corrective continuation, not a
|
|
218
|
+
hard stop guarantee. Broken claims — a
|
|
219
|
+
checked-but-unproven `[review:]` item, a changes-requested or stale
|
|
220
|
+
verdict — block the stop with the reasons fed back; unfinished work
|
|
221
|
+
never does, the same as the gate. The command respects
|
|
222
|
+
`stop_hook_active` (a blocked stop is never re-blocked into a loop) and
|
|
223
|
+
fails open: an internal error exits zero, because a broken hook must
|
|
224
|
+
not trap the session. Claude blocks with exit 2 and stderr; Codex exits 0
|
|
225
|
+
with its documented `Stop` continuation JSON (`decision: "block"` plus
|
|
226
|
+
`reason`); an empty JSON object allows a clean stop. The Codex boundary accepts
|
|
227
|
+
exactly `{}`, or exactly the two keys `decision` and `reason`, where the
|
|
228
|
+
decision is `"block"` and the reason is a string with non-whitespace content.
|
|
229
|
+
It emits compact JSON without changing valid reason text. Extra or missing
|
|
230
|
+
keys, whitespace-only reasons, arrays, primitives, malformed or empty output,
|
|
231
|
+
and nonzero child results all fail open as `{}`.
|
|
232
|
+
Pi treats command failures as fail-open and never sends their output into a
|
|
233
|
+
model turn. Merging rules match the session hook.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stdd/cli",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Spec + Test Driven Development — a markdown-first methodology kit for teams building software with AI coding agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./sdk/index.d.ts",
|
|
9
|
+
"import": "./sdk/index.mjs"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"types": "./sdk/index.d.ts",
|
|
13
|
+
"bin": {
|
|
14
|
+
"stdd": "cli/stdd.mjs"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"cli/",
|
|
18
|
+
"sdk/",
|
|
19
|
+
"method/",
|
|
20
|
+
"playbooks/",
|
|
21
|
+
"templates/",
|
|
22
|
+
"adapters/",
|
|
23
|
+
"prebuilds/"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"test": "node --test",
|
|
27
|
+
"test:harness": "node scripts/agent-contract.mjs",
|
|
28
|
+
"build:plugin": "node scripts/build-plugin.mjs",
|
|
29
|
+
"check": "biome ci .",
|
|
30
|
+
"format": "biome check --fix .",
|
|
31
|
+
"selfcheck": "node cli/stdd.mjs check ."
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=20"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"sdd",
|
|
38
|
+
"tdd",
|
|
39
|
+
"stdd",
|
|
40
|
+
"spec-driven-development",
|
|
41
|
+
"test-driven-development",
|
|
42
|
+
"ai-agents",
|
|
43
|
+
"claude-code",
|
|
44
|
+
"codex",
|
|
45
|
+
"pi-coding-agent",
|
|
46
|
+
"methodology",
|
|
47
|
+
"workflow"
|
|
48
|
+
],
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/vsem-azamat/stdd.git"
|
|
52
|
+
},
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/vsem-azamat/stdd/issues"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://github.com/vsem-azamat/stdd#readme",
|
|
57
|
+
"author": "Azamat Almazbek uulu",
|
|
58
|
+
"license": "MIT",
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@biomejs/biome": "^2.3.8"
|
|
61
|
+
},
|
|
62
|
+
"directories": {
|
|
63
|
+
"test": "test"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stdd-brainstorming
|
|
3
|
+
description: Shape a fuzzy idea into an agreed behavior contract before any plan or code
|
|
4
|
+
when: A non-trivial change is requested and the requirements, scope, or approach are not yet pinned down.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Brainstorming
|
|
8
|
+
|
|
9
|
+
The goal is agreement on **what** and **why** before anyone invests in **how**.
|
|
10
|
+
The output is not a document — it is a shared understanding that becomes a
|
|
11
|
+
docs edit and a PR description.
|
|
12
|
+
|
|
13
|
+
## Process
|
|
14
|
+
|
|
15
|
+
1. **Understand the current state first.** Read the relevant docs and the code
|
|
16
|
+
the change will touch. Questions asked from ignorance waste the other
|
|
17
|
+
side's time; questions asked from knowledge sharpen the idea.
|
|
18
|
+
2. **Ask one question at a time.** Prefer questions that eliminate whole
|
|
19
|
+
branches of the design space: who is it for, what triggers it, what must
|
|
20
|
+
never happen, what is explicitly out of scope. When the answer space is
|
|
21
|
+
enumerable, offer it as a closed choice with your recommendation first —
|
|
22
|
+
a closed question costs the other side seconds, an open one minutes.
|
|
23
|
+
Keep open questions for genuinely open design space.
|
|
24
|
+
3. **Challenge scope creep in both directions.** If the idea is bigger than
|
|
25
|
+
the need, say so and propose the smaller version. If the stated need hides
|
|
26
|
+
a larger real problem, surface it.
|
|
27
|
+
4. **Propose 2–3 approaches with a recommendation.** For each: one paragraph,
|
|
28
|
+
the trade-off that actually matters, and what it costs later. Recommend
|
|
29
|
+
one; do not present a menu without an opinion.
|
|
30
|
+
5. **Converge on the behavior contract.** State the agreed behavior as rules
|
|
31
|
+
precise enough to test. Confirm them explicitly.
|
|
32
|
+
|
|
33
|
+
## Output
|
|
34
|
+
|
|
35
|
+
- The agreed rules become the **docs edit** (the spec) — the first commit of
|
|
36
|
+
the branch.
|
|
37
|
+
- The rationale, rejected alternatives, and scope decisions go into the
|
|
38
|
+
**PR description** when the branch opens.
|
|
39
|
+
- Nothing from this conversation is committed as a standalone file.
|
|
40
|
+
|
|
41
|
+
## Anti-patterns
|
|
42
|
+
|
|
43
|
+
- Jumping to implementation detail while behavior is still unsettled.
|
|
44
|
+
- Asking multiple stacked questions at once.
|
|
45
|
+
- Writing a "spec document" instead of editing the real docs.
|
|
46
|
+
- Agreeing silently: if you disagree with the direction, say so with reasons.
|