@izkac/forgekit 0.3.11 → 0.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@izkac/forgekit",
3
- "version": "0.3.11",
3
+ "version": "0.3.12",
4
4
  "description": "Forgekit CLIs — forgekit (install), forge (workflow), review (code review)",
5
5
  "type": "module",
6
6
  "bin": {
package/src/score.mjs CHANGED
@@ -154,11 +154,13 @@ function scoreProductLoopBody(body, executedGreen = false) {
154
154
  notes.push('no executed green e2e run and no product-loop section in verify-evidence');
155
155
  return { points: 0, max, notes };
156
156
  }
157
- if (/\b(fixture|OP\d+|testdata|sample)\b/i.test(body)) {
157
+ // \w* suffixes: a trailing \b silently rejected inflected forms ("asserts",
158
+ // "fixtures", "ratify") and cost honest sessions real points.
159
+ if (/\b(fixture\w*|OP\d+|testdata\w*|sample\w*)/i.test(body)) {
158
160
  pts += 4;
159
161
  notes.push('names a fixture / corpus');
160
162
  }
161
- if (/\b(differ|baseline|ratif|assert|before.?after|output changed)\b/i.test(body)) {
163
+ if (/\b(differ\w*|baseline\w*|ratif\w*|assert\w*|before.?after|output changed)/i.test(body)) {
162
164
  pts += 5;
163
165
  notes.push('asserts decision/output change vs baseline');
164
166
  } else {
@@ -179,6 +179,32 @@ Fixture: OP1086
179
179
  }
180
180
  });
181
181
 
182
+ test('scoreSession: product-loop bonuses match inflected forms (asserts, fixtures, ratify)', () => {
183
+ const root = tmp('forge-score-inflect-');
184
+ try {
185
+ const { sessionDir, session } = makeSession(root, { slug: 'jobs-loop' });
186
+ fs.writeFileSync(
187
+ path.join(sessionDir, 'spine.json'),
188
+ `${JSON.stringify(validSpine([validRow()]), null, 2)}\n`,
189
+ 'utf8',
190
+ );
191
+ // Every keyword deliberately inflected — a trailing \b used to reject all of these.
192
+ fs.writeFileSync(
193
+ path.join(sessionDir, 'verify-evidence.md'),
194
+ '# Verify\n\n## Product loop\n\n1. seed fixtures\n2. run the loop\n3. asserts the ratify output differs from stored baselines\n',
195
+ 'utf8',
196
+ );
197
+ const card = scoreSession({ cwd: root, sessionDir, session });
198
+ const loop = card.checks.find((c) => c.id === 'product_loop');
199
+ const notes = loop.notes.join('\n');
200
+ assert.match(notes, /names a fixture/);
201
+ assert.match(notes, /asserts decision\/output change/);
202
+ assert.equal(loop.points, 20);
203
+ } finally {
204
+ fs.rmSync(root, { recursive: true, force: true });
205
+ }
206
+ });
207
+
182
208
  test('scoreSession: green e2e run proves the loop even without the phrase', () => {
183
209
  const root = tmp('forge-score-e2e-');
184
210
  try {
@@ -163,6 +163,13 @@ forge defer resolve --task <id> # when the wiring task actually lands
163
163
  - `forge phase done|finish` **refuses** while any deferral is unresolved.
164
164
  - An unregistered "later" in a brief or review is a REJECT.
165
165
 
166
+ **Prose caveats don't survive — checkboxes and deferrals do.** Never tick a
167
+ `tasks.md` box while a note says the work is unproven ("gate was never taken",
168
+ "not yet verified", "should work"). Scorers, gates, and future sessions see
169
+ the `[x]`, not the sentence above it. If a task is incomplete in any way, the
170
+ box **stays open** or the gap becomes a `forge defer` entry — a ticked box
171
+ documenting its own incompleteness is worse than an open one.
172
+
166
173
  ## Spine matrix (mechanical — `forge spine`)
167
174
 
168
175
  **Spine is mandatory for every Forge change** — not gated on pace, and not