feature-factory 0.7.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/LICENSE +21 -0
- package/README.md +278 -0
- package/WORKFLOW.md +2001 -0
- package/agents/backend-builder.md +102 -0
- package/agents/codebase-researcher.md +122 -0
- package/agents/design-interpreter.md +71 -0
- package/agents/frontend-builder.md +110 -0
- package/agents/implementation-validator.md +78 -0
- package/agents/spec-writer.md +95 -0
- package/agents/story-reader.md +70 -0
- package/agents/story-writer.md +62 -0
- package/agents/test-verifier.md +94 -0
- package/agents/work-decomposer.md +188 -0
- package/agents/work-reviewer.md +131 -0
- package/bin/factory.js +1499 -0
- package/bin/init-publication.js +73 -0
- package/core/atomic-write.js +135 -0
- package/core/contracts.js +394 -0
- package/core/effective-push.js +88 -0
- package/core/executable.js +29 -0
- package/core/run-lock.js +269 -0
- package/core/write-core.js +146 -0
- package/observe/index.js +366 -0
- package/observe/repair-record.js +300 -0
- package/observe/repair-reverification.js +169 -0
- package/observe/repository-config.js +56 -0
- package/observe/review.js +362 -0
- package/package.json +35 -0
- package/state/index.js +64 -0
- package/state/review-archive.js +48 -0
- package/state/schema.js +339 -0
- package/state/session-lock.js +104 -0
- package/state/transition.js +26 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 opencode-feature-factory contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# feature-factory
|
|
2
|
+
|
|
3
|
+
A durable, observed control plane for running a feature from idea to draft PR through a chain of
|
|
4
|
+
focused agents, with human approval gates. Host-agnostic, zero dependencies.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
npm install feature-factory
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Ships three things:
|
|
11
|
+
|
|
12
|
+
| | |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `bin/factory.js` | the `factory` CLI — fourteen commands, each state change one checked transition |
|
|
15
|
+
| `WORKFLOW.md` | the canonical, host-agnostic workflow contract |
|
|
16
|
+
| `agents/` | eleven specialist definitions dispatched by a host adapter |
|
|
17
|
+
|
|
18
|
+
This package owns `WORKFLOW.md`; it does not ship a platform `SKILL.md`. Each adapter owns its own
|
|
19
|
+
host binding and copies this workflow beside that skill at build/pack time. Install
|
|
20
|
+
`opencode-feature-factory` or `prime-agent-feature-factory` for a supported host, or build an adapter
|
|
21
|
+
that loads the complete workflow and drives all durable state changes through the CLI.
|
|
22
|
+
|
|
23
|
+
## Repository command configuration
|
|
24
|
+
|
|
25
|
+
A repository operator may provide optional `$O/.factory.json`, where `O` is the physically
|
|
26
|
+
resolved Git top level:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"resolve": "<non-empty shell command>",
|
|
31
|
+
"verify": "<non-empty shell command>",
|
|
32
|
+
"publish": "<non-empty shell command>",
|
|
33
|
+
"publishing_identity": "<non-empty account name>",
|
|
34
|
+
"pr_draft": true,
|
|
35
|
+
"verify_timeout_ms": 900000,
|
|
36
|
+
"bootstrap": "<non-empty shell command>",
|
|
37
|
+
"bootstrap_timeout_ms": 900000
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The root has four required properties and four optional properties: `pr_draft`, `verify_timeout_ms`,
|
|
42
|
+
`bootstrap`, and `bootstrap_timeout_ms`. `resolve`, `verify`, `publish`, and a present `bootstrap` are non-empty command
|
|
43
|
+
strings. `publishing_identity` is a static non-empty account name, not a command, token, credential, or
|
|
44
|
+
command result. A present `pr_draft` must be a JSON boolean and omission means `true`. Both timeouts are
|
|
45
|
+
positive safe integers. `bootstrap_timeout_ms` requires `bootstrap`.
|
|
46
|
+
Each omitted timeout independently defaults to `900000`; neither shares the other's budget. The file is
|
|
47
|
+
operator-owned, committed, and protected as a privileged path: a run cannot create, write, merge,
|
|
48
|
+
archive, package, or repair it.
|
|
49
|
+
|
|
50
|
+
Validation refuses the first matching defect in this order: unreadable or invalid JSON, a non-object root, or unknown keys; invalid `pr_draft`; invalid `bootstrap`; `bootstrap_timeout_ms` without `bootstrap`; invalid `bootstrap_timeout_ms`; invalid `verify_timeout_ms`; then missing or invalid required entries.
|
|
51
|
+
|
|
52
|
+
The named forms are `.factory.json entry 'pr_draft' must be a boolean`, `.factory.json entry 'bootstrap' must be a non-empty string`, `.factory.json entry 'bootstrap_timeout_ms' requires a declared bootstrap command`, `.factory.json entry 'bootstrap_timeout_ms' must be a positive integer`, and `.factory.json entry 'verify_timeout_ms' must be a positive integer`.
|
|
53
|
+
|
|
54
|
+
A present invalid, unreadable, incomplete, wrong-type, whitespace-only, or unknown-property config refuses closed.
|
|
55
|
+
|
|
56
|
+
Fresh init captures the effective `pr_draft` value only after the cloned repository config validates,
|
|
57
|
+
and stores that boolean immutably in `run.json`; init JSON and plain output do not change. Legacy
|
|
58
|
+
manifests without the key remain keyless and behave as `true`. Status alone adds effective
|
|
59
|
+
`pr_draft: boolean` in JSON and `pr_draft: true|false` in plain output. Step 6 reads that status value:
|
|
60
|
+
`true` (including legacy omission) creates a draft PR, while explicit `false` creates a ready-for-review
|
|
61
|
+
PR without `--draft`. Publication does not reread the live config.
|
|
62
|
+
|
|
63
|
+
`resolve`, `bootstrap`, and `verify` are consumed now. `resolve` runs as one ordinary shell step with the configured string submitted
|
|
64
|
+
unchanged, repository-root cwd, inherited environment plus the exact admitted request in
|
|
65
|
+
`FACTORY_INPUT`, and no positional argument or structured stdin. Empty stdout means the input was not
|
|
66
|
+
recognized. Non-empty stdout is the direct,
|
|
67
|
+
unchanged `ISSUE_PAYLOAD`: one JSON object containing a valid canonical top-level string `run_id`, a
|
|
68
|
+
non-empty string `title`, and a string `body` — all three validated before the run id is bound or anything
|
|
69
|
+
is dispatched — where `run_id`
|
|
70
|
+
selects the run and reaches `story-reader` without extraction, wrapping, reserialization, or
|
|
71
|
+
normalization. The value matches `^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$`; digit-only values are positive
|
|
72
|
+
decimal without leading zeroes.
|
|
73
|
+
|
|
74
|
+
Malformed config, malformed payload, a non-zero exit, or unavailable exit status refuses before any
|
|
75
|
+
run effect and never falls back:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
invalid factory config: .factory.json; no session or run created.
|
|
79
|
+
factory config entry 'resolve' returned malformed payload for reference <reference>; no session or run created.
|
|
80
|
+
factory config entry 'resolve' failed for reference <reference> with exit status <status>; no session or run created.
|
|
81
|
+
factory config entry 'resolve' failed for reference <reference>; exit status unavailable; no session or run created.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Resolver diagnostics name `resolve`, the status classification, and the admitted reference bounded to 200
|
|
85
|
+
characters; neither the configured or expanded
|
|
86
|
+
command line, shell diagnostics, nor credentials are printed, logged, or persisted. Credential values
|
|
87
|
+
stay in inherited environment variables and never in the config. The resolver contract adds no bridge,
|
|
88
|
+
parser service, command runner, capture or stderr policy, output channel or size policy, buffering,
|
|
89
|
+
truncation, redaction, timeout, retry, cache, payload transport, or session behavior.
|
|
90
|
+
|
|
91
|
+
### Sandbox bootstrap
|
|
92
|
+
|
|
93
|
+
Only the CLI executes configured `bootstrap`: once during fresh init after clone, containment, and PR-base observation but before manifest publication, and again on every explicit resume while the run remains parked. The exact configured string runs unchanged with `shell: true`, inherited environment and stdin, cwd exactly the selected sandbox, and child stdout and stderr both routed to CLI stderr. Init JSON stdout therefore remains exactly one response object. Bootstrap has its own configured timeout or independent `900000` millisecond default, with no retry and no use of the verify budget.
|
|
94
|
+
|
|
95
|
+
After every attempt, the CLI checks tracked worktree and index paths only and ignores untracked dependency output. Unobservable tracked state refuses before dirty tracked paths; dirty paths refuse before unavailable or nonzero exit, and diagnostics name exact repository-relative paths. A clean numeric zero stores paired `bootstrap_command` and `bootstrap_exit` manifest evidence. The command is exact; the exit is a non-negative integer or `null`. Ordinary transitions preserve the pair, and status output does not expose it.
|
|
96
|
+
|
|
97
|
+
A failed, timed-out, dirty, or unobservable fresh init emits no JSON stdout, retains the deterministic sandbox, and leaves `run.json` absent. Configured resume first binds exact raw manifest bytes, validated parked state, a forward timestamp, and the exact fresh owner. Clean zero records evidence and unparks while preserving progress and the historical terminal result. Ordinary failure with intact bindings records integer or `null` evidence, advances the timestamp, remains `needs-human`, preserves progress and the historical result, and refuses so a later explicit resume reruns bootstrap. Byte mutation or owner loss preserves current state and ownership, records no evidence, and does not unpark. Factory claim, force-steal, heartbeat refresh, and release serialize with resume publication through `run-json.lock`.
|
|
98
|
+
|
|
99
|
+
When both bootstrap keys are absent, init and resume are exact no-ops for bootstrap: no execution, manifest fields, output, or response-shape change.
|
|
100
|
+
|
|
101
|
+
Bootstrap never runs during resolver intake, merge verification or replay, direct repository verification, slice observation, Gate 3, effective push, configured publication, push, or PR creation. Existing resolver, verify, configured-publish, effective-push, push, PR, and Gate 3 behavior is unchanged.
|
|
102
|
+
|
|
103
|
+
After a slice merge is successfully and atomically recorded, `verify` starts in the exact recorded
|
|
104
|
+
integration worktree. Its configured string is submitted unchanged as one ordinary shell command with
|
|
105
|
+
that worktree as cwd and inherited environment and stdio. Stdout and stderr are visible, informational,
|
|
106
|
+
and unparsed; they are not captured or persisted. Numeric child exit status is authoritative: zero
|
|
107
|
+
succeeds and non-zero fails repository verification; no numeric status is canonical unavailable
|
|
108
|
+
evidence. Each repository shell attempt gets the full configured timeout. A merge or replay invocation
|
|
109
|
+
executes at most twice, and retries only a first unavailable result after freshly proving the unchanged
|
|
110
|
+
worktree, merge SHA at `HEAD`, and clean tree. Resolver, slice, and Gate 3 commands receive neither this
|
|
111
|
+
timeout nor this retry. The observation uses the existing canonical `evidence/test-verifier.json` schema,
|
|
112
|
+
bound to the current merged head and the immutable root base.
|
|
113
|
+
|
|
114
|
+
If `.factory.json` is absent, intake declares no resolver and init and resume perform no bootstrap. After
|
|
115
|
+
a recorded merge the factory silently returns its previous response with no repository command,
|
|
116
|
+
evidence write, or new output. A post-record
|
|
117
|
+
failure leaves the merged row and its slice evidence and review unchanged and stops before the next wave.
|
|
118
|
+
Production defects, repair exhaustion, dirty or moved safety failures, invalid config, and malformed,
|
|
119
|
+
stale, foreign, wrong-command, missing-field, or inconsistent evidence terminalize `needs-human`; clean,
|
|
120
|
+
unchanged second-unavailable repository verification instead follows the nonterminal exhausted/release
|
|
121
|
+
contract below. Only a confirmed test-only finding can use the bounded test-file-only repair path, with a
|
|
122
|
+
separate commit and fresh repository verification.
|
|
123
|
+
|
|
124
|
+
Canonical evidence has exactly four classifications. `green` has exact run, `test-verifier` subject,
|
|
125
|
+
current merged head, and unchanged `verify` command binding, observed integer exit zero, and
|
|
126
|
+
`review_ready: true`. `failed` has that exact binding with an observed nonzero integer exit, or observed
|
|
127
|
+
zero that is not review-ready. `unavailable` has that exact binding and `observed: false`, `exit: null`,
|
|
128
|
+
and `skipped_reason: null`. Missing, unreadable, malformed, foreign, stale-head, wrong-command,
|
|
129
|
+
missing-field, or internally inconsistent evidence is `unknown`. Only `unavailable` is replay-eligible.
|
|
130
|
+
A later invocation may replay the exact same-SHA merge only with no active repair, a freshly verified
|
|
131
|
+
exact integration worktree on the recorded feature branch, the immutable merge SHA still at `HEAD`, and
|
|
132
|
+
a freshly observed clean tree. Green and failed evidence is reused; unknown evidence never executes. The
|
|
133
|
+
merged slice is never reopened, re-seeded, re-observed, remerged, or re-dispatched.
|
|
134
|
+
|
|
135
|
+
Two clean, unchanged unavailable executions terminate the current CLI and driver invocations, not the
|
|
136
|
+
durable run. The driver quiesces all tasks and heartbeats, releases exactly its owning session, and uses
|
|
137
|
+
qualified status to prove `running`, null terminal result, and no ownership before reporting
|
|
138
|
+
`repository-verify-exhausted`. Release or ownership-verification failure reports `retained-lock-error`
|
|
139
|
+
without a resumability claim. A later invocation repeats all normal guards, claims with its actual
|
|
140
|
+
host-exported session ID, verifies ownership, and only then reconciles the same SHA. That value may equal
|
|
141
|
+
the prior value; verified release followed by a new verified claim establishes freshness.
|
|
142
|
+
|
|
143
|
+
Gate 3 always runs a separate fresh integrated `test-verifier` observation at the current head. It
|
|
144
|
+
overwrites canonical evidence through the existing command mode and never shares, substitutes, or
|
|
145
|
+
optimizes from post-merge evidence, even when the head is unchanged.
|
|
146
|
+
|
|
147
|
+
## Resuming a parked run
|
|
148
|
+
|
|
149
|
+
A top-level `needs-human` status is a parked stop, not a final result. `completed`, `partial`, and
|
|
150
|
+
`blocked` remain final. Fix the external cause, then preserve this order: bind the intended retained
|
|
151
|
+
sandbox and validate its manifest and containment; run the post-selection exact-ref-absent guard;
|
|
152
|
+
complete effective-push proof; accept branch provenance, worktree binding, seed ancestry, cleanliness,
|
|
153
|
+
recovery, and every operator-ref recheck; rerun the final exact-ref guard immediately before claim;
|
|
154
|
+
claim or justifiably steal and verify fresh ownership plus the unchanged parked result; run
|
|
155
|
+
an optional `factory amend-paths <run-id> <slice-id> --add <path> [--add <path> ...] --reason <text>
|
|
156
|
+
--session <id> --repo <sandbox>` only when the verified cause is missing ownership on an unmerged slice;
|
|
157
|
+
then run `factory resume <run-id> --session <id> --repo <sandbox>` and verify running status, unchanged historical result, real
|
|
158
|
+
next action, and the same owner; replay only existing post-lock reconciliation for an already-recorded
|
|
159
|
+
merge; then continue solely from the newly qualified `status.next`.
|
|
160
|
+
|
|
161
|
+
Status remains readable while parked, and lock claim, justified steal, heartbeat, and owning release
|
|
162
|
+
remain available for ordered qualification. Every other effectful command refuses until explicit
|
|
163
|
+
resume. Without configured bootstrap, resume changes only `status` and `updated_at`. With configured
|
|
164
|
+
bootstrap, it also records the paired command and result under the bound transition described above.
|
|
165
|
+
Both paths preserve the original terminal reason and all progress. A resumed recorded merge still traverses the existing clean-head, retry-safety, evidence, and
|
|
166
|
+
repository-verification path. An unresolved repair-journal record is separate and remains
|
|
167
|
+
publication-blocking. If the cause was not fixed, the run may park again with the same reason.
|
|
168
|
+
|
|
169
|
+
The optional amendment runs while the status remains `needs-human`, after fresh exact owner
|
|
170
|
+
verification and before resume. It appends repository-relative, nonprivileged paths in request order and
|
|
171
|
+
records their verbatim reason, session, and timestamp in `path_amendments`; the original path prefix and
|
|
172
|
+
`test_plan` stay immutable. It never resolves or requires path existence, and another slice may already
|
|
173
|
+
own the same path. Duplicate, target-already-owned, malformed, privileged, replayed, or merged-slice
|
|
174
|
+
requests refuse atomically. Resume never amends or reseeds. A merge continues to refuse every unamended
|
|
175
|
+
or privileged changed path.
|
|
176
|
+
|
|
177
|
+
`resolve`, `verify`, and `publishing_identity` are consumed now. Configured `publish` remains unconsumed and is not invoked.
|
|
178
|
+
Effective push-target capture and comparison are active through the package-owned `factory effective-push` command; they are not deferred to configured `publish`.
|
|
179
|
+
The validated raw `publishing_identity` is retained exactly as parsed, without trimming, normalization,
|
|
180
|
+
case-folding, or reserialization. A missing, non-string, or whitespace-only identity makes a present
|
|
181
|
+
config malformed. `publishing_identity` itself adds no config key or syntax, run status, or factory command. The independent `factory effective-push` command adds no state or flag.
|
|
182
|
+
|
|
183
|
+
With a present valid config, every mode checks that identity at exactly three boundaries: immediately
|
|
184
|
+
after verified post-lock ownership, or immediately after an explicit resume is verified running with
|
|
185
|
+
the same fresh owner and before reconciliation or other work; immediately before `git push`, after
|
|
186
|
+
effective push-target equality; and immediately before `gh pr create`, after the push is known
|
|
187
|
+
successful. No operation intervenes across a guard boundary. An absent config skips all three guards and
|
|
188
|
+
preserves existing behavior.
|
|
189
|
+
|
|
190
|
+
Before each guard, inherited `GH_TOKEN` must exist and contain at least one character. Missing or empty
|
|
191
|
+
means identity is unobservable without invoking `gh`, the network, stored authentication, credential
|
|
192
|
+
queries, or any fallback. A prepared environment submits exactly this read-only network probe as one
|
|
193
|
+
ordinary host shell step with cwd exactly `RUN_REPO`, inherited environment, and no stdin:
|
|
194
|
+
|
|
195
|
+
```sh
|
|
196
|
+
gh api --method GET /user --jq .login
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The direct stdout bytes, stderr bytes, and numeric status are parsed strictly. Only numeric zero, empty
|
|
200
|
+
stderr, and exactly one ASCII GitHub login plus one LF are observable; the required LF alone is removed,
|
|
201
|
+
then the raw login is compared exactly and case-sensitively with the raw declaration. `gh auth status`
|
|
202
|
+
does not prove the publishing identity.
|
|
203
|
+
|
|
204
|
+
A mismatch names the safely rendered declared and observed values and says to authenticate as the
|
|
205
|
+
declared account; an unobservable result names the safely rendered declaration and says to launch with
|
|
206
|
+
inherited `GH_TOKEN` for it. Values use deterministic ASCII-only JSON-string rendering, including
|
|
207
|
+
lowercase `\uXXXX` escapes outside printable ASCII. The complete rendered reason is transported as one
|
|
208
|
+
shell-safe argv token and the sole `--reason` value. The token, raw stdout or stderr, diagnostics, status,
|
|
209
|
+
targets, helper output, and environment are never exposed, and the factory never manages credentials or
|
|
210
|
+
transport.
|
|
211
|
+
|
|
212
|
+
Either refusal quiesces outstanding work, parks through existing `needs-human`, verifies the exact
|
|
213
|
+
persisted reason and owner, releases that owner, verifies the lock absent, and retains the sandbox. After
|
|
214
|
+
the environment is fixed, a later driver binds the retained sandbox and repeats every selection,
|
|
215
|
+
manifest, containment, config, effective-push, provenance, branch, worktree, cleanliness or recovery,
|
|
216
|
+
and exact-ref precheck; makes and verifies a fresh claim with its own `FACTORY_SESSION_ID`; runs exactly
|
|
217
|
+
`factory resume "$R" --session "$FACTORY_SESSION_ID" --repo "$RUN_REPO"`; verifies running status, the
|
|
218
|
+
unchanged historical result, real next action, and same owner; performs existing post-resume
|
|
219
|
+
reconciliation; and continues only from the newly qualified `status.next`. It never reuses the released
|
|
220
|
+
session.
|
|
221
|
+
|
|
222
|
+
Publishing-identity verification is enforcement because it prevents false-green or wrong-account
|
|
223
|
+
publication. Credential provisioning and helper setup are instruction only. Existing push,
|
|
224
|
+
`gh pr create`, `factory pr`, Gate 3, merge, and approval semantics remain unchanged. The live config
|
|
225
|
+
is not part of this package and no generated config or resolver asset is shipped. See the repository's
|
|
226
|
+
[operator guide](https://github.com/jasoncarreira/feature-factory/blob/main/OPERATING.md) for the
|
|
227
|
+
shared inherited-token helper recipe; it does not acquire, store, install, or repair credentials.
|
|
228
|
+
|
|
229
|
+
## Effective push target
|
|
230
|
+
|
|
231
|
+
```text
|
|
232
|
+
factory effective-push <bootstrap|check> <operator-repository> <sandbox-repository>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The command accepts exactly those three positional arguments and no options. `bootstrap` captures the
|
|
236
|
+
operator's effective push target, configures the sandbox push URL from it, then freshly captures both
|
|
237
|
+
repositories and compares them exactly. `check` freshly captures both targets and compares without
|
|
238
|
+
configuration. Both modes use shell-free Git subprocesses, write no output on success, and retain the
|
|
239
|
+
sandbox on a fixed redacted failure. Captured targets and child diagnostics are never returned, logged,
|
|
240
|
+
persisted in factory state, printed, or attached as an error cause. The command is independent of
|
|
241
|
+
`publishing_identity`, adds no run state or flag, and configured `publish` remains unconsumed.
|
|
242
|
+
|
|
243
|
+
## Why the code exists at all
|
|
244
|
+
|
|
245
|
+
Almost all of this system is prose. Code exists only where prose cannot enforce something:
|
|
246
|
+
|
|
247
|
+
- **Agents cannot reliably hand-write a schema-perfect `run.json`**, so every state change goes
|
|
248
|
+
through `lock → read → validate → apply → validate → compare-and-swap → rename`, and nothing else
|
|
249
|
+
writes the manifest.
|
|
250
|
+
- **Verification exists only where its absence produces a false green.** With a human at the gate,
|
|
251
|
+
someone sees the diff. In an autonomous run nobody does, so a review must name the commit it
|
|
252
|
+
judged, a merge must prove it contributed exactly what was reviewed, and a test result must have
|
|
253
|
+
been observed rather than reported.
|
|
254
|
+
|
|
255
|
+
Run state lives at `<repo>/.factory/<run-id>/run.json`, which must be gitignored: a tracked
|
|
256
|
+
control plane puts manifest churn in every slice diff and trips the privileged-path refusal on every
|
|
257
|
+
merge.
|
|
258
|
+
|
|
259
|
+
## The read-only API
|
|
260
|
+
|
|
261
|
+
For tools that display run state. Everything here reads; nothing writes.
|
|
262
|
+
|
|
263
|
+
```js
|
|
264
|
+
import { readRun, readRunUnchecked, nextAction, validateRun, RUN_KEYS } from "feature-factory";
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`readRun` validates and throws; `readRunUnchecked` reports a broken record instead of refusing to
|
|
268
|
+
load it, so a diagnostic can show an operator what is wrong. `nextAction` derives what happens next,
|
|
269
|
+
and is the same function `factory status` uses — so a display cannot disagree with the CLI.
|
|
270
|
+
|
|
271
|
+
The write path is deliberately not exported. Changing state means calling the CLI.
|
|
272
|
+
|
|
273
|
+
See the [repository](https://github.com/jasoncarreira/feature-factory) for the full command
|
|
274
|
+
reference and design notes.
|
|
275
|
+
|
|
276
|
+
## License
|
|
277
|
+
|
|
278
|
+
MIT
|