pincer-workflow 0.7.0 → 1.0.1
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/README.md +161 -193
- package/bin/pincer.js +189 -166
- package/package.json +4 -3
- package/template/.agents/skills/pincer-blueprint/SKILL.md +70 -0
- package/template/.agents/skills/pincer-status/SKILL.md +29 -55
- package/template/.claude/commands/pincer-blueprint.md +7 -0
- package/template/.claude/commands/pincer-status.md +4 -59
- package/template/.claude/hooks/hook-policy.cjs +60 -25
- package/template/.claude/hooks/ticket-guard.sh +6 -3
- package/template/.claude/pincer/library/agents/accessibility-reviewer.md +27 -0
- package/template/.claude/pincer/library/agents/code-quality-reviewer.md +25 -0
- package/template/.claude/pincer/library/agents/codebase-explorer.md +23 -0
- package/template/.claude/pincer/library/agents/fix-agent.md +23 -0
- package/template/.claude/pincer/library/agents/history-reviewer.md +26 -0
- package/template/.claude/pincer/library/agents/implementer.md +24 -0
- package/template/.claude/pincer/library/agents/security-reviewer.md +26 -0
- package/template/.claude/pincer/library/agents/silent-failure-hunter.md +27 -0
- package/template/.claude/pincer/library/agents/standards-reviewer.md +25 -0
- package/template/.claude/pincer/library/agents/test-coverage-reviewer.md +26 -0
- package/template/.claude/pincer/library/agents-skeleton.md +89 -0
- package/template/.claude/pincer/library/agreement.md +46 -0
- package/template/.claude/pincer/library/models.json +6 -0
- package/template/.claude/pincer/library/stages/blueprint/SKILL.md +69 -0
- package/template/.claude/pincer/library/stages/blueprint/formats/harness.md +82 -0
- package/template/.claude/pincer/library/stages/blueprint/formats/roster-rules.md +61 -0
- package/template/.claude/pincer/library/stages/blueprint/prompts/confirm-kind.md +19 -0
- package/template/.claude/pincer/library/stages/blueprint/prompts/first-job.md +18 -0
- package/template/.claude/pincer/library/stages/blueprint/prompts/q-handoffs.md +21 -0
- package/template/.claude/pincer/library/stages/blueprint/prompts/q-models.md +20 -0
- package/template/.claude/pincer/library/stages/blueprint/prompts/q-pipeline.md +27 -0
- package/template/.claude/pincer/library/stages/blueprint/prompts/q-repo.md +21 -0
- package/template/.claude/pincer/library/stages/blueprint/prompts/q-responsibility.md +30 -0
- package/template/.claude/pincer/library/stages/blueprint/prompts/q-roster.md +20 -0
- package/template/.claude/pincer/library/stages/blueprint/prompts/q-simplify.md +26 -0
- package/template/.claude/pincer/library/stages/blueprint/prompts/q-systems.md +28 -0
- package/template/.claude/pincer/library/stages/code/SKILL.md +59 -0
- package/template/.claude/pincer/library/stages/code/formats/pr.md +55 -0
- package/template/.claude/pincer/library/stages/code/prompts/implement.md +25 -0
- package/template/.claude/pincer/library/stages/evaluate/SKILL.md +51 -0
- package/template/.claude/pincer/library/stages/evaluate/formats/evidence.md +49 -0
- package/template/.claude/pincer/library/stages/evaluate/formats/finding-report.md +72 -0
- package/template/.claude/pincer/library/stages/evaluate/formats/finding.md +35 -0
- package/template/.claude/pincer/library/stages/evaluate/prompts/review.md +25 -0
- package/template/.claude/pincer/library/stages/narrow/SKILL.md +47 -0
- package/template/.claude/pincer/library/stages/narrow/formats/ticket.md +126 -0
- package/template/.claude/pincer/library/stages/narrow/prompts/explore.md +19 -0
- package/template/.claude/pincer/library/stages/plan/SKILL.md +46 -0
- package/template/.claude/pincer/library/stages/plan/formats/spec.md +100 -0
- package/template/.claude/pincer/library/stages/plan/prompts/explore.md +20 -0
- package/template/.claude/pincer/library/stages/release/SKILL.md +51 -0
- package/template/.claude/pincer/library/stages/status/SKILL.md +37 -0
- package/template/.claude/skills/pincer-blueprint/SKILL.md +69 -0
- package/template/.claude/skills/pincer-blueprint/formats/harness.md +82 -0
- package/template/.claude/skills/pincer-blueprint/formats/roster-rules.md +61 -0
- package/template/.claude/skills/pincer-blueprint/prompts/confirm-kind.md +19 -0
- package/template/.claude/skills/pincer-blueprint/prompts/first-job.md +18 -0
- package/template/.claude/skills/pincer-blueprint/prompts/q-handoffs.md +21 -0
- package/template/.claude/skills/pincer-blueprint/prompts/q-models.md +20 -0
- package/template/.claude/skills/pincer-blueprint/prompts/q-pipeline.md +27 -0
- package/template/.claude/skills/pincer-blueprint/prompts/q-repo.md +21 -0
- package/template/.claude/skills/pincer-blueprint/prompts/q-responsibility.md +30 -0
- package/template/.claude/skills/pincer-blueprint/prompts/q-roster.md +20 -0
- package/template/.claude/skills/pincer-blueprint/prompts/q-simplify.md +26 -0
- package/template/.claude/skills/pincer-blueprint/prompts/q-systems.md +28 -0
- package/template/.claude/skills/pincer-status/SKILL.md +37 -0
- package/template/.codex/README.md +39 -35
- package/template/.github/copilot-instructions.md +19 -14
- package/template/.github/prompts/pincer-blueprint.prompt.md +70 -0
- package/template/.github/prompts/pincer-status.prompt.md +29 -55
- package/template/AGENTS.md +34 -67
- package/template/docs/engine.md +249 -0
- package/template/docs/release-checklist.md +27 -28
- package/template/scripts/pincer/blueprint.cjs +137 -0
- package/template/scripts/pincer/cli.cjs +233 -0
- package/template/scripts/pincer/evidence.cjs +142 -0
- package/template/scripts/pincer/generate.cjs +375 -0
- package/template/scripts/pincer/harness.cjs +269 -0
- package/template/scripts/pincer/models.cjs +121 -0
- package/template/scripts/{pincer-runtime → pincer}/parse.cjs +83 -133
- package/template/scripts/pincer/recon.cjs +205 -0
- package/template/scripts/pincer/status.cjs +173 -0
- package/template/scripts/pincer/ticket.cjs +204 -0
- package/template/scripts/pincer/util.cjs +104 -0
- package/template/scripts/pincer-blueprint.cjs +7 -0
- package/template/scripts/pincer-evidence.cjs +5 -73
- package/template/scripts/pincer-status.sh +10 -12
- package/template/scripts/pincer-ticket.sh +15 -18
- package/template/scripts/sync-prompts.sh +2 -50
- package/template/.agents/skills/pincer-code/SKILL.md +0 -240
- package/template/.agents/skills/pincer-evaluate/SKILL.md +0 -178
- package/template/.agents/skills/pincer-narrow/SKILL.md +0 -148
- package/template/.agents/skills/pincer-plan/SKILL.md +0 -134
- package/template/.agents/skills/pincer-release/SKILL.md +0 -82
- package/template/.claude/agents/code-quality-reviewer.md +0 -38
- package/template/.claude/agents/codebase-explorer.md +0 -23
- package/template/.claude/commands/pincer-code.md +0 -238
- package/template/.claude/commands/pincer-evaluate.md +0 -176
- package/template/.claude/commands/pincer-narrow.md +0 -146
- package/template/.claude/commands/pincer-plan.md +0 -132
- package/template/.claude/commands/pincer-release.md +0 -80
- package/template/.claude/references/prd-template.md +0 -108
- package/template/.claude/references/ticket-template.md +0 -86
- package/template/.github/prompts/pincer-code.prompt.md +0 -240
- package/template/.github/prompts/pincer-evaluate.prompt.md +0 -178
- package/template/.github/prompts/pincer-narrow.prompt.md +0 -148
- package/template/.github/prompts/pincer-plan.prompt.md +0 -134
- package/template/.github/prompts/pincer-release.prompt.md +0 -82
- package/template/docs/dry-run-checklist.md +0 -264
- package/template/docs/runtime-contracts.md +0 -1769
- package/template/scripts/pincer-runtime/adopt.cjs +0 -132
- package/template/scripts/pincer-runtime/agreement.cjs +0 -240
- package/template/scripts/pincer-runtime/authorization.cjs +0 -167
- package/template/scripts/pincer-runtime/changes.cjs +0 -517
- package/template/scripts/pincer-runtime/checks.cjs +0 -48
- package/template/scripts/pincer-runtime/coverage.cjs +0 -361
- package/template/scripts/pincer-runtime/dispositions.cjs +0 -95
- package/template/scripts/pincer-runtime/evidence.cjs +0 -676
- package/template/scripts/pincer-runtime/fsutil.cjs +0 -37
- package/template/scripts/pincer-runtime/gates.cjs +0 -73
- package/template/scripts/pincer-runtime/identity.cjs +0 -163
- package/template/scripts/pincer-runtime/impact.cjs +0 -177
- package/template/scripts/pincer-runtime/io.cjs +0 -41
- package/template/scripts/pincer-runtime/lifecycle.cjs +0 -311
- package/template/scripts/pincer-runtime/locator.cjs +0 -158
- package/template/scripts/pincer-runtime/migrate.cjs +0 -204
- package/template/scripts/pincer-runtime/phases.cjs +0 -245
- package/template/scripts/pincer-runtime/readiness.cjs +0 -102
- package/template/scripts/pincer-runtime/requirements.cjs +0 -255
- package/template/scripts/pincer-runtime/resume.cjs +0 -273
- package/template/scripts/pincer-runtime/routing.cjs +0 -54
- package/template/scripts/pincer-runtime/runner.cjs +0 -242
- package/template/scripts/pincer-runtime/sanitize.cjs +0 -63
- package/template/scripts/pincer-runtime/scaffold.cjs +0 -254
- package/template/scripts/pincer-runtime/source.cjs +0 -129
- package/template/scripts/pincer-runtime/state.cjs +0 -314
- package/template/scripts/pincer-runtime/status.cjs +0 -514
- package/template/scripts/pincer-runtime/transaction.cjs +0 -200
- package/template/scripts/pincer-runtime/transitions.cjs +0 -134
- package/template/scripts/pincer-runtime.cjs +0 -686
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# The PINCER engine
|
|
2
|
+
|
|
3
|
+
The contract between the stage skills and the scripts under `scripts/pincer/`. The
|
|
4
|
+
skills are prose for an agent and bend to the agreement; the engine is the part that
|
|
5
|
+
does not bend. It stamps the receipts, records the acceptance, validates the evidence
|
|
6
|
+
and generates the project's copies. Read this when you want to know exactly what a
|
|
7
|
+
script does, or when a session has to recover from a mistake.
|
|
8
|
+
|
|
9
|
+
## What the engine is
|
|
10
|
+
|
|
11
|
+
Four entry points over one dependency-free Node core (`scripts/pincer/`, Node 22 or
|
|
12
|
+
newer, CommonJS, `node:` modules only). `scripts/pincer-ticket.sh` moves a ticket
|
|
13
|
+
through its lifecycle and is the only writer of its lifecycle fields.
|
|
14
|
+
`scripts/pincer-status.sh` reports where the work stands and gates the stages.
|
|
15
|
+
`scripts/pincer-evidence.cjs` validates and digests evidence.
|
|
16
|
+
`scripts/pincer-blueprint.cjs` reads the repository (recon), writes the agreement
|
|
17
|
+
(write) and generates the project copies (generate); `scripts/sync-prompts.sh` is
|
|
18
|
+
`generate` under its old name. Every entry point resolves the project root from
|
|
19
|
+
`--root <dir>`, else the nearest directory up from the working directory that holds
|
|
20
|
+
`harness.json`, else the working directory. The engine never talks to the forge; the
|
|
21
|
+
skills do, with `gh`.
|
|
22
|
+
|
|
23
|
+
## harness.json
|
|
24
|
+
|
|
25
|
+
The machine-readable agreement at the repository root, written by the blueprint stage
|
|
26
|
+
through `write` and read by every other stage. Schema 1. Unknown keys are kept and
|
|
27
|
+
ignored. `blueprint` and `status` never appear under `stages`: they are always on and
|
|
28
|
+
the validator rejects an entry for them.
|
|
29
|
+
|
|
30
|
+
| Field | Values | Default |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `schema` | `1` | required |
|
|
33
|
+
| `project.kind` | `greenfield`, `brownfield` | required |
|
|
34
|
+
| `project.name`, `project.reason` | text | `""` |
|
|
35
|
+
| `people[]` | `{name, role, accepts}`, at least one with `accepts: true` | required |
|
|
36
|
+
| `systems.forge` | `github`, `none`, `other` | `none` |
|
|
37
|
+
| `systems.remote` | URL without credentials | `""` |
|
|
38
|
+
| `systems.ci` | true, false | `false` |
|
|
39
|
+
| `systems.deploy`, `systems.secrets` | text | `""` |
|
|
40
|
+
| `repo.branches` | `per-ticket`, `main` | `main` |
|
|
41
|
+
| `repo.pr` | true, false | `false` |
|
|
42
|
+
| `repo.merge` | `merge`, `squash`, `rebase` | `merge` |
|
|
43
|
+
| `repo.protection` | text | `""` |
|
|
44
|
+
| `repo.checks` | list of commands | `[]` |
|
|
45
|
+
| `homes.specs`, `homes.tickets`, `homes.evidence` | relative paths inside the repository, no leading `/` or `..` | `specs`, `tickets`, `evidence` |
|
|
46
|
+
| `stages.plan` | `{on, spec: file or forge, gate, reason}` | `on: true, spec: file, gate: ""` |
|
|
47
|
+
| `stages.narrow` | `{on, tickets: file or forge, reason}` | `on: true, tickets: file` |
|
|
48
|
+
| `stages.code` | `{on, implementer, reason}` | `on: true, implementer: false` |
|
|
49
|
+
| `stages.evaluate` | `{on, where: local or pr, evidence, reason}` | `on: true, where: local, evidence: true` |
|
|
50
|
+
| `stages.release` | `{on, audit: tree or pr, reason}` | `on: true, audit: tree` |
|
|
51
|
+
| `roster.plan`, `roster.narrow`, `roster.code`, `roster.evaluate` | lists of agent names from the library | `[]` |
|
|
52
|
+
| `models.stages.<stage>` | a tier or `{tier, effort}`; `plan`, `narrow`, `code`, `evaluate`, `release` | not set |
|
|
53
|
+
| `models.agents.<name>` | a tier or `{tier, effort}`, for a library agent name | not set |
|
|
54
|
+
| `models.map.<platform>.<tier>` | a model name, for `claude`, `codex`, `copilot` and `strong`, `standard`, `fast` | none |
|
|
55
|
+
| `open` | list of text | `[]` |
|
|
56
|
+
| `firstJob` | text | `""` |
|
|
57
|
+
|
|
58
|
+
A file that fails validation makes every entry point print `harness.json: <problem>`
|
|
59
|
+
and exit 3. A missing file prints `harness.json: no agreement yet: run /pincer-blueprint`
|
|
60
|
+
and exits 3, except for `status`, which reports it and exits 0, and the always-on gates.
|
|
61
|
+
|
|
62
|
+
## Ticket fields and receipts
|
|
63
|
+
|
|
64
|
+
A ticket is `<homes.tickets>/T-NN-<slug>.md` with a closed `---` frontmatter of
|
|
65
|
+
unique, unindented `key: value` lines; `#` starts a comment, except in
|
|
66
|
+
`accepted_words`, which is read verbatim. Required keys: `ticket`, `status`, `size`
|
|
67
|
+
(`S`, `M`, `L`), `depends_on` (an inline list such as `[T-01]`). Optional: `spec` (a
|
|
68
|
+
relative `.md` path that must exist), `forge` (free text), `timeout` (seconds, default
|
|
69
|
+
600). The body needs one `## Acceptance Criteria` section with checkboxes and one
|
|
70
|
+
`## Verification` section with one fenced `bash` block.
|
|
71
|
+
|
|
72
|
+
The eight lifecycle fields are written only by `scripts/pincer-ticket.sh`:
|
|
73
|
+
|
|
74
|
+
| Field | Written by | Format |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `status` | start, done | `open`, `in_progress`, `done` |
|
|
77
|
+
| `started` | start | ISO UTC timestamp, `YYYY-MM-DDTHH:MM:SSZ` |
|
|
78
|
+
| `last_check` | verify | `<timestamp> running|passed|failed|interrupted <hash>` |
|
|
79
|
+
| `verified` | verify, on a pass | `<timestamp> <hash>` |
|
|
80
|
+
| `finished` | done | timestamp |
|
|
81
|
+
| `accepted` | accept | timestamp |
|
|
82
|
+
| `accepted_by` | accept | a name from `people` with `accepts: true` |
|
|
83
|
+
| `accepted_words` | accept | what the person said, verbatim, on one line |
|
|
84
|
+
|
|
85
|
+
The hash is the first 12 hex characters of sha256 over the Verification block: the
|
|
86
|
+
lines inside the fence joined with newlines plus one trailing newline. Change the
|
|
87
|
+
block and the receipt no longer matches it. `verify` writes `running` first, runs the
|
|
88
|
+
block with `bash -eo pipefail` under the ticket's timeout, then writes `passed`
|
|
89
|
+
(together with `verified`), `failed`, or `interrupted` for a signal or a timeout. A
|
|
90
|
+
ticket is ready when `verified` carries the current hash and the `last_check` for that
|
|
91
|
+
hash is not a failure.
|
|
92
|
+
|
|
93
|
+
## Commands and exit codes
|
|
94
|
+
|
|
95
|
+
Exit codes everywhere: 0 ok, 1 refused (the reason on stderr), 2 usage, 3 no
|
|
96
|
+
agreement, stage off or invalid `harness.json`, 130 interrupted.
|
|
97
|
+
|
|
98
|
+
`scripts/pincer-ticket.sh` runs `node scripts/pincer/cli.cjs ticket <verb> <T-NN>`:
|
|
99
|
+
- `start T-NN`: `open` to `in_progress`, stamps `started`. Refuses when a `depends_on` ticket is not done and ready, when the ticket is already done, and with exit 3 when `harness.json` is missing or `stages.code.on` is false.
|
|
100
|
+
- `verify T-NN`: starts an open ticket, runs the block, stamps the receipt only on exit 0. A failed check exits 1 and prints `FAILED (exit N)`; a block with no runnable command is refused.
|
|
101
|
+
- `done T-NN`: `in_progress` to `done` when the receipt matches the current block, no newer check failed, and every criterion is ticked; stamps `finished`.
|
|
102
|
+
- `accept T-NN --by "<name>" --words "<their words>"`: on a done ticket writes the three acceptance fields together; refuses a name that is not an acceptor and empty words.
|
|
103
|
+
|
|
104
|
+
`scripts/pincer-status.sh` runs `node scripts/pincer/cli.cjs status`: the report, or
|
|
105
|
+
`--json` for data. `--stage <name>` prints `on` and exits 0, or the reason and exits 3;
|
|
106
|
+
a name that is not a stage exits 2. An invalid `harness.json` still renders the report
|
|
107
|
+
and then exits 3.
|
|
108
|
+
|
|
109
|
+
`scripts/pincer-evidence.cjs` runs `node scripts/pincer/cli.cjs evidence`:
|
|
110
|
+
`validate <manifest> [--candidate <sha>]` prints `ok <candidate>` or one
|
|
111
|
+
`evidence: <manifest>: <problem>` line each and exits 1; `digest <file>...` prints
|
|
112
|
+
`<sha256> <file>` per file.
|
|
113
|
+
|
|
114
|
+
`scripts/pincer-blueprint.cjs` has three verbs:
|
|
115
|
+
- `recon [--json] [--network]` runs `node scripts/pincer/cli.cjs recon`: the kind, the reasons, the facts, the prefill and what it could not see. Offline unless `--network`, which lets it ask `gh`.
|
|
116
|
+
- `write [--answers <file>]` runs `node scripts/pincer/cli.cjs write`: validates the answers (default: `harness.json` itself), writes `harness.json`, renders the agreement between the markers in `AGENTS.md`, runs the generator. Validation problems: one per line, exit 3, nothing written. One agreement marker without the other: exit 1, nothing written.
|
|
117
|
+
- `generate [--harness <file>] [--platform <claude,codex,copilot|all>] [--overwrite]` runs `node scripts/pincer/cli.cjs generate`, also reachable as `scripts/sync-prompts.sh`. See Generation below.
|
|
118
|
+
|
|
119
|
+
## Stage gate
|
|
120
|
+
|
|
121
|
+
Every stage skill's first step runs `scripts/pincer-status.sh --stage <name>`.
|
|
122
|
+
`blueprint` and `status` are always on and print `on` even without `harness.json`. The
|
|
123
|
+
other five (`plan`, `narrow`, `code`, `evaluate`, `release`) print `on` when the
|
|
124
|
+
agreement has them on, else the reason (`no agreement yet: run /pincer-blueprint`,
|
|
125
|
+
`stage <name> is off in harness.json`, or the validation problem) with exit 3.
|
|
126
|
+
`scripts/pincer-ticket.sh start` and `verify` apply the same gate for `code`. The hook
|
|
127
|
+
on Claude Code does not read `harness.json`; the gate lives in the scripts, so it holds
|
|
128
|
+
on every platform.
|
|
129
|
+
|
|
130
|
+
## Acceptance
|
|
131
|
+
|
|
132
|
+
A ticket is `done` only on a receipt and accepted only when a named person said so.
|
|
133
|
+
`accept` writes `accepted`, `accepted_by` and `accepted_words` together; the validator
|
|
134
|
+
refuses a partial trio. The name must be in `people` with `accepts: true`; quote the
|
|
135
|
+
person's words verbatim. Acceptance does not block the next ticket, but status warns
|
|
136
|
+
about each done ticket without it and points to release (or `accept` if release is off).
|
|
137
|
+
Release passes only when every done ticket is accepted. Run `accept` only after the person says so; it never commits or publishes.
|
|
138
|
+
|
|
139
|
+
Release first audits read-only, allowing only `done without acceptance` warnings; all other warnings fail.
|
|
140
|
+
Record the manifest's evaluated `candidate` and current `audit_head` separately; only retained evidence
|
|
141
|
+
and existing acceptance fields may differ, never implementation, checks, spec or agreement.
|
|
142
|
+
Without evidence, `audit_head` is the candidate. The technical audit starts and ends with a clean tree.
|
|
143
|
+
|
|
144
|
+
After the person accepts, inspect working-tree, staged and committed diffs against `audit_head`:
|
|
145
|
+
only `accepted`, `accepted_by` and `accepted_words` in those tickets' frontmatter may change.
|
|
146
|
+
Commit separately as `accept: <candidate short sha>`, recording its full `acceptance_commit`;
|
|
147
|
+
skip an empty commit when nothing was newly accepted. Require a clean tree, status without warnings
|
|
148
|
+
and unchanged evidence validated against the original candidate. The verdict names both identities;
|
|
149
|
+
acceptance is not a newly tested candidate. Unexpected changes stop release for re-evaluation.
|
|
150
|
+
|
|
151
|
+
Branch and PR rules still apply: a required acceptance PR must be merged with passing checks before PASS.
|
|
152
|
+
Never push, merge or publish without the user's authorization. Unanswered acceptance or an incomplete
|
|
153
|
+
required remote step is pending, not passed.
|
|
154
|
+
|
|
155
|
+
## Evidence manifest schema 1
|
|
156
|
+
|
|
157
|
+
`<homes.evidence>/<candidate>/manifest.json`, where the directory's basename is the
|
|
158
|
+
candidate's full commit id. Top-level keys, all required and no others: `schema` (1),
|
|
159
|
+
`candidate` and `base` (40 hex characters), `created` (UTC timestamp), `spec` (a path
|
|
160
|
+
that exists), `environment` (`tools` and `limitations`, each a list of short
|
|
161
|
+
strings), `checks`, `visual_review`, `artifacts`. Each check has `id` (`C-NN`), `kind` (`command`, `review`, `visual`),
|
|
162
|
+
`required`, `result` (`passed`, `failed`, `unverified`), `command` (required for a
|
|
163
|
+
command check), `log` (an artifact path) and an optional `note`; a required check
|
|
164
|
+
must be `passed`. `visual_review` has `applicable` and, when false, `reason`.
|
|
165
|
+
`artifacts` maps paths inside the manifest's directory to their sha256, and the
|
|
166
|
+
digests must match the files. The validator checks that the record is consistent, not
|
|
167
|
+
that the commands ran. Status reports `Evidence:` per manifest and release reads the
|
|
168
|
+
same validator.
|
|
169
|
+
|
|
170
|
+
## Generation and .new sidecars
|
|
171
|
+
|
|
172
|
+
`generate` reads the library (`.claude/pincer/library/`, the project's own copy) and
|
|
173
|
+
the agreement. For every stage that is on it copies `library/stages/<stage>/` to
|
|
174
|
+
`.claude/skills/pincer-<stage>/` (the project copy: edit this one), writes the thin
|
|
175
|
+
`.claude/commands/pincer-<stage>.md`, and per installed platform the Codex skill
|
|
176
|
+
`.agents/skills/pincer-<stage>/SKILL.md` (`/pincer-x` mentions become `$pincer-x`,
|
|
177
|
+
`$ARGUMENTS` becomes a sentence) and the Copilot prompt
|
|
178
|
+
`.github/prompts/pincer-<stage>.prompt.md`, both derived from the project copy as it
|
|
179
|
+
stands. Agents named by the rosters of the stages that are on are rendered to
|
|
180
|
+
`.claude/agents/<name>.md`: the library copy with its `tier:` line dropped and the
|
|
181
|
+
model and effort of its tier appended; with Codex installed each also gets
|
|
182
|
+
`.codex/agents/<name>.toml` (see Models). The model and effort lines of an agent file
|
|
183
|
+
and of a project copy belong to the generator and the tier belongs in the agreement: a
|
|
184
|
+
file that differs from the library copy only in those lines is the generator's to
|
|
185
|
+
overwrite or remove when no baseline records it; with a baseline in `.pincer.json`, a
|
|
186
|
+
hand-written model line conflicts like any other edit and gets a sidecar.
|
|
187
|
+
|
|
188
|
+
Baselines live in `.pincer.json` under `generated` when the installer wrote it. A
|
|
189
|
+
target whose hash matches its baseline or the fresh content is overwritten; one that
|
|
190
|
+
matches neither was edited by hand and is kept, with the fresh content beside it as
|
|
191
|
+
`<file>.new`. Derived files carry the comment
|
|
192
|
+
`<!-- Generated by scripts/pincer/cli.cjs generate from <source> ... -->`; a file that
|
|
193
|
+
still carries it is the generator's to overwrite or remove. The files of a stage
|
|
194
|
+
switched off, or of an agent dropped from every active roster, are removed when
|
|
195
|
+
unedited and reported when not. `--overwrite` treats every target as unedited.
|
|
196
|
+
Platforms come from `.pincer.json`, all three without one; `--platform` overrides them
|
|
197
|
+
for one run. Kit maintainers regenerate the shipped copies the same way, with
|
|
198
|
+
`--root template`, `--platform all` and a `--harness` file in which every optional
|
|
199
|
+
stage is off; a test fails when the shipped copies are stale.
|
|
200
|
+
|
|
201
|
+
## Models
|
|
202
|
+
|
|
203
|
+
The agreement names a tier per role under `models.stages.<stage>` (the five optional
|
|
204
|
+
stages) and `models.agents.<name>`: `strong`, `standard`, `fast`, `inherit` for the
|
|
205
|
+
session's model, or `""` for not set, each with an optional effort `low`, `medium` or
|
|
206
|
+
`high`. The library's `models.json` maps a tier to each platform's model name: the
|
|
207
|
+
Claude row holds the aliases `opus`, `sonnet`, `haiku`; the Codex and Copilot rows name
|
|
208
|
+
the vendors' current models and change when a vendor ships. `models.map.<platform>.<tier>`
|
|
209
|
+
in the agreement overrides one entry. A library agent file may carry `tier:` as the
|
|
210
|
+
kit's default for that role (`codebase-explorer`: `standard`). Resolution, in
|
|
211
|
+
`scripts/pincer/models.cjs`: the agreement's tier, else the library's, else not set.
|
|
212
|
+
`inherit` and not set write no model field, so the host's own order stands; an effort
|
|
213
|
+
is written whenever set, with or without a model. The always-on stages take no tier:
|
|
214
|
+
their copies are installer-owned and the validator rejects an entry for them.
|
|
215
|
+
|
|
216
|
+
What the generator writes. Claude Code: `model:` and `effort:` in the agent files and,
|
|
217
|
+
for a stage with a tier, in its project copy and thin command; the model applies to the
|
|
218
|
+
stage's first turn and the session's model resumes on the next prompt. Codex:
|
|
219
|
+
`.codex/agents/<name>.toml` per roster agent, with `model` and
|
|
220
|
+
`model_reasoning_effort`, and one hint line at the top of the stage skill, which has no
|
|
221
|
+
model field. Copilot: `model:` in the prompt file; the agents are the Claude files,
|
|
222
|
+
which VS Code reads and maps itself. A tier the table cannot translate, or a missing
|
|
223
|
+
table, is reported once and the field is left out; doctor prints the resolved mapping
|
|
224
|
+
per installed platform. The host decides last: Claude Code's model allowlist and
|
|
225
|
+
subagent env vars, Copilot org policy and Codex project trust override any file.
|
|
226
|
+
|
|
227
|
+
| Role | Claude Code | Codex | Copilot (VS Code) |
|
|
228
|
+
|---|---|---|---|
|
|
229
|
+
| agent model | frontmatter, under the host's resolution order | agent file | the Claude file, mapped by VS Code |
|
|
230
|
+
| agent effort | frontmatter | agent file | not available |
|
|
231
|
+
| stage model | first turn of the stage | a hint in the skill | the prompt file |
|
|
232
|
+
| stage effort | frontmatter | the same hint | not available |
|
|
233
|
+
|
|
234
|
+
## Recovery
|
|
235
|
+
|
|
236
|
+
- A failed check: fix the cause and run `verify` again. The failed `last_check` stays until a pass replaces it; it is information, not damage.
|
|
237
|
+
- An interrupted check (a signal, the timeout, exit 130): run `verify` again.
|
|
238
|
+
- A ticket that status reports as `invalid`, or whose lifecycle fields were edited by hand: the person repairs the file in their own terminal, then `verify` runs again (and `done` again if it was done). The session reports the problem and never edits those fields itself.
|
|
239
|
+
- A check that changed after it passed: `done` refuses with both hashes; run `verify` again.
|
|
240
|
+
- The rule under all of these: you never restore a ticket file from git (`checkout`, `restore`, `reset --hard`, `stash`) to clear a warning, because that revives an old receipt as if it were new evidence. On Claude Code the hook blocks it; elsewhere it is the standing rule in `AGENTS.md`.
|
|
241
|
+
- `write` refused: fix the answers file and run it again; nothing was written. One agreement marker without the other in `AGENTS.md`: put the missing marker back by hand, then `write`.
|
|
242
|
+
- A `.new` sidecar: compare it with the project copy, take what you want, delete the sidecar. `generate` writes a new one only when the fresh content differs again.
|
|
243
|
+
|
|
244
|
+
## Platform notes
|
|
245
|
+
|
|
246
|
+
- Claude Code: `.claude/settings.json` wires two PreToolUse hooks. `block-dangerous.sh` refuses destructive commands. `ticket-guard.sh` refuses edits to the eight lifecycle fields and any shell command that writes, resets or restores a ticket file, allowing only exact calls of the project's own `scripts/pincer-ticket.sh start|verify|done|accept` and the `cli.cjs ticket` form at the same path; a lookalike script elsewhere is refused. It also refuses edits and shell writes to the engine (`scripts/pincer-*`, `scripts/pincer/`) and to the hooks (`.claude/hooks/`, `.claude/settings.json`), which `npx pincer-workflow update` refreshes. The guard matches `T-NN-*.md` under any directory named `tickets`; a `homes.tickets` that does not end in `tickets` loses hook coverage, and the rule in `AGENTS.md` carries the weight alone. The hook does not read `harness.json`. It is a guardrail against documented mistake forms, not a security boundary: the sandbox and the approval controls remain that boundary.
|
|
247
|
+
- Codex: reads `AGENTS.md` natively; the stages are skills under `.agents/skills/`, invoked as `$pincer-<stage>`; the roster agents are `.codex/agents/<name>.toml`, generated from the library, and Codex loads `.codex/` only in a project marked trusted. No hook. Posture in `~/.codex/config.toml`: `approval_policy = "on-request"` and `sandbox_mode = "workspace-write"`; never run with approvals disabled. Status warns about missing, failed or stale receipts and about hand-edited fields.
|
|
248
|
+
- Copilot: `.github/copilot-instructions.md` points at `AGENTS.md`; the stages are prompt files under `.github/prompts/`, invoked as `/pincer-<stage>` with prompt files enabled; the roster agents are the files under `.claude/agents/`, which VS Code reads in the Claude format. No hook; the same rule.
|
|
249
|
+
- Everywhere: Node 22 or newer, git and bash. `gh` only for the forge-facing steps the skills name. The engine runs offline.
|
|
@@ -1,38 +1,37 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Release checklist
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
`/pincer-release` first audits without writing files, then records the person's
|
|
4
|
+
acceptance and commits only those acceptance fields. Technical checks must pass
|
|
5
|
+
before asking for acceptance. Mark acceptance items pending until the person has
|
|
6
|
+
responded; pending acceptance alone is not a technical audit failure. Final PASS
|
|
7
|
+
requires every applicable item below. Give one sentence of evidence per item.
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## Tickets
|
|
8
10
|
|
|
9
|
-
- [ ]
|
|
10
|
-
- [ ]
|
|
11
|
-
- [ ]
|
|
12
|
-
- [ ]
|
|
13
|
-
- [ ] Change records only: the selected change is `completed`, its authorization is `current` for the current agreement, `.prd/evidence/changes/<id>.json` names the evaluated candidate, and the audit selected, activated and completed nothing
|
|
14
|
-
- [ ] Every file the evidence manifest lists is tracked; the working tree is clean before and after the audit
|
|
15
|
-
- [ ] Strict coverage only (`Coverage strict …` in status): `node scripts/pincer-runtime.cjs coverage` reports structure complete, every in-scope scenario `delivered` on the evaluated candidate, every deferral or removal backed by its decision and user authorization, `delivery` distinguishing original from agreed scope, and an `adequate` adequacy judgment; `ready` names `COVERAGE_INCOMPLETE`, `SCOPE_UNAUTHORIZED`, `OBLIGATION_MISSING`, `REVIEW_MISSING` or `ADEQUACY_REQUIRED` as failures. A change without the capability is labeled `unverified`: say so, never imply strict coverage
|
|
11
|
+
- [ ] status prints the agreement; before acceptance, its only permitted warnings are `T-NN done without acceptance`; after acceptance there are no warnings
|
|
12
|
+
- [ ] every ticket of the change is `done` with `verified` on its status line
|
|
13
|
+
- [ ] no ticket line reads `invalid`
|
|
14
|
+
- [ ] `git log --oneline` shows one `T-NN: <title>` commit per ticket, in dependency order
|
|
16
15
|
|
|
17
|
-
##
|
|
16
|
+
## Evidence
|
|
18
17
|
|
|
19
|
-
- [ ]
|
|
20
|
-
- [ ]
|
|
21
|
-
- [ ]
|
|
22
|
-
- [ ]
|
|
18
|
+
- [ ] record the full evaluated `candidate` and pre-acceptance `audit_head`; their diff contains only retained evaluation artifacts and previously recorded acceptance fields, with no implementation, check, spec or agreement changes
|
|
19
|
+
- [ ] `git status --short` is empty before and after the read-only audit; no check wrote files or moved `HEAD`
|
|
20
|
+
- [ ] with tree audit, every command in `repo.checks` passes when run directly, output quoted
|
|
21
|
+
- [ ] with evidence on, `node scripts/pincer-evidence.cjs validate <manifest> --candidate <candidate>` prints `ok <candidate>` and every listed artifact is tracked; repeat after acceptance against the same original candidate
|
|
22
|
+
- [ ] with PR audit, the implementation PR is merged with green checks and the named deploy was observed; any acceptance PR required by the agreement is also merged with its required checks passing before final PASS
|
|
23
23
|
|
|
24
24
|
## Review
|
|
25
25
|
|
|
26
|
-
- [ ]
|
|
27
|
-
- [ ]
|
|
28
|
-
- [ ]
|
|
29
|
-
- [ ] Security review reports locations and remediation without printing candidate secret values
|
|
30
|
-
- [ ] No secret environment file is tracked; dependency and invalid-input checks run when applicable
|
|
26
|
+
- [ ] the review report exists and every finding has a file, a line and a disposition
|
|
27
|
+
- [ ] every implementation fix after evaluate went through its own ticket and `T-NN:` commit, followed by a new evaluation
|
|
28
|
+
- [ ] no secret environment file is tracked: `git ls-files | grep -i '^\.env'` shows only `.env.example`, or no entries when no environment example is needed
|
|
31
29
|
|
|
32
|
-
##
|
|
30
|
+
## Acceptance
|
|
33
31
|
|
|
34
|
-
- [ ]
|
|
35
|
-
- [ ]
|
|
36
|
-
- [ ]
|
|
37
|
-
- [ ]
|
|
38
|
-
- [ ]
|
|
32
|
+
- [ ] the person saw the result and every ticket's `Done when` lines before being asked
|
|
33
|
+
- [ ] every done ticket shows `accepted by <name>` in status, the name from `people` with `accepts: true`
|
|
34
|
+
- [ ] `accepted_words` holds the person's own words, not a paraphrase or synthetic approval
|
|
35
|
+
- [ ] only `accept` wrote acceptance; the complete staged and committed diff from `audit_head` changes only `accepted`, `accepted_by`, and `accepted_words` in the intended tickets' frontmatter, never their body, receipt or any other file
|
|
36
|
+
- [ ] new acceptance is retained in a separate `accept: <candidate short sha>` commit; the verdict identifies both the evaluated candidate and acceptance commit, and `git status --short` is empty
|
|
37
|
+
- [ ] no push, merge or publication occurred without the user's authorization; any required remote acceptance step is complete or explicitly pending rather than passed
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// PINCER blueprint — the writer (docs/engine.md, "Blueprint"). Reads an answers
|
|
3
|
+
// file in the shape of harness.json, validates it, writes harness.json, renders
|
|
4
|
+
// the agreement from library/agreement.md between the markers in AGENTS.md and
|
|
5
|
+
// runs the generator. Everything outside the markers is left byte for byte.
|
|
6
|
+
const fs = require('node:fs');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
const util = require('./util.cjs');
|
|
9
|
+
const harness = require('./harness.cjs');
|
|
10
|
+
const generate = require('./generate.cjs');
|
|
11
|
+
const models = require('./models.cjs');
|
|
12
|
+
|
|
13
|
+
const AGREEMENT_START = '<!-- pincer:agreement:start -->';
|
|
14
|
+
const AGREEMENT_END = '<!-- pincer:agreement:end -->';
|
|
15
|
+
const AGENTS_FILE = 'AGENTS.md';
|
|
16
|
+
const { LIBRARY, libraryDir } = generate;
|
|
17
|
+
const TEMPLATE_FILE = 'agreement.md';
|
|
18
|
+
const SKELETON_FILE = 'agents-skeleton.md';
|
|
19
|
+
const CODE_GATE = 'passing check per ticket';
|
|
20
|
+
|
|
21
|
+
// {{slot}} and {{#each list}}…{{/each}} with {{.}} for the item; nothing else.
|
|
22
|
+
function fill(template, view) {
|
|
23
|
+
const lookup = (key) => {
|
|
24
|
+
if (!(key in view)) throw new Error(`${TEMPLATE_FILE}: unknown slot {{${key}}}`);
|
|
25
|
+
return view[key];
|
|
26
|
+
};
|
|
27
|
+
const expanded = template.replace(/\{\{#each ([\w.]+)\}\}\n?([\s\S]*?)\{\{\/each\}\}\n?/g, (_, key, block) => {
|
|
28
|
+
const items = lookup(key);
|
|
29
|
+
if (!Array.isArray(items)) throw new Error(`${TEMPLATE_FILE}: {{#each ${key}}} needs a list`);
|
|
30
|
+
return items.map((item) => block.replace(/\{\{\.\}\}/g, String(item))).join('');
|
|
31
|
+
});
|
|
32
|
+
return expanded.replace(/\{\{([\w.]+)\}\}/g, (_, key) => {
|
|
33
|
+
const value = lookup(key);
|
|
34
|
+
if (Array.isArray(value)) throw new Error(`${TEMPLATE_FILE}: {{${key}}} is a list; use {{#each}}`);
|
|
35
|
+
return String(value);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const or = (value, fallback) => (typeof value === 'string' && value.trim() ? value.trim() : fallback);
|
|
40
|
+
const yesNo = (value) => (value ? 'yes' : 'no');
|
|
41
|
+
|
|
42
|
+
// The one-line pipeline: the stages that run, in order, each gate in bold.
|
|
43
|
+
function pipeline(h) {
|
|
44
|
+
const who = harness.acceptors(h);
|
|
45
|
+
const gates = {
|
|
46
|
+
plan: or(h.stages.plan.gate, 'spec approved'),
|
|
47
|
+
code: CODE_GATE,
|
|
48
|
+
evaluate: h.stages.evaluate.evidence === false ? 'review done' : 'evidence saved',
|
|
49
|
+
release: `accepted by ${who.length ? who.join(', ') : 'a person'}`,
|
|
50
|
+
};
|
|
51
|
+
return harness.STAGES.filter((name) => harness.stageOn(h, name))
|
|
52
|
+
.map((name) => (gates[name] ? `${name} (**${gates[name]}**)` : name))
|
|
53
|
+
.join(' → ');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// The slots of library/agreement.md from a normalized agreement; the library
|
|
57
|
+
// supplies the agents' default tiers for the Models line.
|
|
58
|
+
function view(h, lib) {
|
|
59
|
+
const homes = harness.homes(h);
|
|
60
|
+
const simplify = harness.OPTIONAL_STAGES.filter((name) => !harness.stageOn(h, name))
|
|
61
|
+
.map((name) => `${name} is off${or(h.stages[name].reason, '') ? ` — ${h.stages[name].reason.trim()}` : ''}.`);
|
|
62
|
+
const people = h.people.map((p) => `${p.name.trim()}${or(p.role, '') ? ` (${p.role.trim()})` : ''}${p.accepts === true ? ' — accepts results' : ''}`);
|
|
63
|
+
const forge = { github: 'GitHub', none: 'none', other: 'other' }[h.systems.forge] || h.systems.forge;
|
|
64
|
+
return {
|
|
65
|
+
name: or(h.project.name, 'unnamed project'),
|
|
66
|
+
kind: h.project.kind,
|
|
67
|
+
kindReason: or(h.project.reason, '') ? ` — ${h.project.reason.trim()}` : '',
|
|
68
|
+
forge,
|
|
69
|
+
remote: or(h.systems.remote, 'none'),
|
|
70
|
+
ci: yesNo(h.systems.ci),
|
|
71
|
+
deploy: or(h.systems.deploy, 'none'),
|
|
72
|
+
secrets: or(h.systems.secrets, 'not recorded'),
|
|
73
|
+
pipeline: pipeline(h),
|
|
74
|
+
spec: !harness.stageOn(h, 'plan') ? 'none (plan is off)' : h.stages.plan.spec === 'forge' ? 'forge' : `${homes.specs}/`,
|
|
75
|
+
tickets: h.stages.narrow.tickets === 'forge' ? 'forge' : `${homes.tickets}/`,
|
|
76
|
+
evidence: !harness.stageOn(h, 'evaluate') ? 'none (evaluate is off)' : h.stages.evaluate.evidence === false ? 'none' : `${homes.evidence}/`,
|
|
77
|
+
simplify: simplify.length ? simplify : ['Every stage runs; nothing switched off.'],
|
|
78
|
+
people: people.length ? people : ['nobody named yet'],
|
|
79
|
+
branches: h.repo.branches === 'per-ticket' ? 'one branch per ticket' : 'commit to main',
|
|
80
|
+
pr: yesNo(h.repo.pr),
|
|
81
|
+
merge: h.repo.merge,
|
|
82
|
+
protection: or(h.repo.protection, 'none'),
|
|
83
|
+
checks: h.repo.checks.length ? h.repo.checks.join(', ') : 'none',
|
|
84
|
+
open: h.open.length ? h.open : ['none'],
|
|
85
|
+
firstJob: or(h.firstJob, 'not named yet'),
|
|
86
|
+
models: models.agreementLine(models.effective(h, lib)),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// The agreement text, without the markers; ends with one newline.
|
|
91
|
+
function renderAgreement(h, { library } = {}) {
|
|
92
|
+
const lib = library || libraryDir(process.cwd());
|
|
93
|
+
const template = fs.readFileSync(path.join(lib, TEMPLATE_FILE), 'utf8');
|
|
94
|
+
return `${fill(template, view(harness.normalize(h), lib)).trimEnd()}\n`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// AGENTS.md with the agreement between the markers. Markers present: the text
|
|
98
|
+
// between them is replaced. No markers: the block goes after a leading title,
|
|
99
|
+
// else at the top. One marker alone is a problem, nothing is changed.
|
|
100
|
+
function splice(text, agreement) {
|
|
101
|
+
const block = `${AGREEMENT_START}\n${agreement}${AGREEMENT_END}`;
|
|
102
|
+
const start = text.indexOf(AGREEMENT_START), end = text.indexOf(AGREEMENT_END);
|
|
103
|
+
if (start === -1 && end === -1) {
|
|
104
|
+
const title = /^# [^\n]*\n/.exec(text);
|
|
105
|
+
const head = title ? title[0] : '';
|
|
106
|
+
const rest = text.slice(head.length).replace(/^\n+/, '');
|
|
107
|
+
return { text: `${head}${head ? '\n' : ''}${block}\n${rest ? `\n${rest}` : ''}` };
|
|
108
|
+
}
|
|
109
|
+
if (start === -1 || end === -1 || end < start) return { problem: `${AGENTS_FILE}: found one agreement marker without the other (${AGREEMENT_START} … ${AGREEMENT_END})` };
|
|
110
|
+
return { text: `${text.slice(0, start)}${block}${text.slice(end + AGREEMENT_END.length)}` };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function write(root, { answers = harness.HARNESS_FILE, label, library } = {}) {
|
|
114
|
+
const name = label || answers;
|
|
115
|
+
const file = path.isAbsolute(answers) ? answers : path.join(root, answers);
|
|
116
|
+
let raw;
|
|
117
|
+
try { raw = fs.readFileSync(file, 'utf8'); } catch (error) {
|
|
118
|
+
return { ok: false, exit: 3, problems: [`${name}: cannot read (${error.code === 'ENOENT' ? 'no such file' : error.message})`] };
|
|
119
|
+
}
|
|
120
|
+
let doc;
|
|
121
|
+
try { doc = JSON.parse(raw); } catch (error) { return { ok: false, exit: 3, problems: [`${name}: invalid JSON (${error.message})`] }; }
|
|
122
|
+
const problems = harness.validate(doc);
|
|
123
|
+
if (problems.length) return { ok: false, exit: 3, problems: problems.map((p) => `${name}: ${p}`) };
|
|
124
|
+
const normalized = harness.normalize(doc);
|
|
125
|
+
const lib = libraryDir(root, library);
|
|
126
|
+
const agreement = renderAgreement(normalized, { library: lib });
|
|
127
|
+
const agentsFile = path.join(root, AGENTS_FILE);
|
|
128
|
+
const current = fs.existsSync(agentsFile) ? fs.readFileSync(agentsFile, 'utf8') : fs.readFileSync(path.join(lib, SKELETON_FILE), 'utf8');
|
|
129
|
+
const spliced = splice(current, agreement);
|
|
130
|
+
if (spliced.problem) return { ok: false, exit: 1, problems: [spliced.problem] };
|
|
131
|
+
util.atomicWrite(path.join(root, harness.HARNESS_FILE), `${JSON.stringify(normalized, null, 2)}\n`);
|
|
132
|
+
util.atomicWrite(agentsFile, spliced.text);
|
|
133
|
+
const generated = generate.generate(root, { harness: normalized, library: lib });
|
|
134
|
+
return { ok: true, harness: normalized, agreement, generated, written: [harness.HARNESS_FILE, AGENTS_FILE] };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = { AGREEMENT_START, AGREEMENT_END, AGENTS_FILE, LIBRARY, write, renderAgreement, splice, fill, libraryDir };
|