yam-flow 1.9.1 → 2.0.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/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  All notable changes to yam-flow are documented here.
4
4
 
5
+ ## v2.0.0 - 2026-06-24
6
+
7
+ ### Added
8
+
9
+ - Extended `yam loop report --json` with lightweight requirement coverage, `failure_cause`, `recovery_hint`, and `avoidance_note` so handoff artifacts can show what must still be covered before a completion claim.
10
+ - Added `readiness_receipt` to `yam release report --json` to summarize the read-only evidence basis for package/version, registry, auth, git, dist, tarball, and release-check readiness.
11
+
12
+ ### Improved
13
+
14
+ - Uncovered loop requirements now block verified truth, default `blocked_kind` to `requirement_uncovered`, and point the default next action at the first uncovered requirement.
15
+ - Documented that loop avoidance notes are report-only; durable learning remains explicit through `yam memory add`.
16
+
17
+ ### Verification
18
+
19
+ - `npm run typecheck`
20
+ - `npm run build`
21
+ - `npm run cli-smoke`
22
+ - `npm run verify:self`
23
+ - `node ./dist/bin/yam.js loop report --json` with uncovered requirement truth blocking
24
+ - `node ./dist/bin/yam.js release report --json` (blocked as expected by npm auth readiness and dirty git, with `readiness_receipt`)
25
+
5
26
  ## v1.9.1 - 2026-06-19
6
27
 
7
28
  ### Added
package/COMMANDS.md CHANGED
@@ -148,7 +148,7 @@ yam tools doctor /path/to/project
148
148
  yam tools doctor /path/to/project --json
149
149
  yam context pressure /path/to/project --json
150
150
  yam cleanup scan /path/to/project --json
151
- yam loop report --route quick --intent "fix release readiness" --stage "inspect:passed:read release report" --evidence "typecheck passed" --evidence-level local --evidence-stamp "sha256:release-report" --blocked-kind auth_blocked --safe-retry "retry after npm whoami succeeds" --fix-first-item "npm auth must be verified before publish" --remaining-task "rerun release report after auth refresh" --recommended-direction "fix npm auth first, then publish manually" --implementation-note "keep loop report read-only" --why-this-next "auth blocks public release claims" --blocked-by "npm whoami E401" --owner-route deep --owner-scope "release readiness only" --scope-owner deep --side-effect "no publish attempted" --issue-code "src/bin/yam.ts release report" --issue-role "summarizes release readiness without publishing" --issue-symptom "npm auth failure needs clearer next action" --changed-code "yam loop report" --changed-role "records loop evidence and learning note" --change-summary "added a read-only loop artifact" --why-important "it helps users learn what changed without overclaiming verification" --learning-note "fix blockers before claiming done" --json
151
+ yam loop report --route quick --intent "fix release readiness" --stage "inspect:passed:read release report" --evidence "typecheck passed" --evidence-level local --evidence-stamp "sha256:release-report" --covered-requirement "release report is read-only" --uncovered-requirement "npm auth verified" --blocked-kind auth_blocked --failure-cause auth_token_invalid --safe-retry "retry after npm whoami succeeds" --recovery-hint "refresh npm auth, then rerun readiness checks" --fix-first-item "npm auth must be verified before publish" --remaining-task "rerun release report after auth refresh" --recommended-direction "fix npm auth first, then publish manually" --implementation-note "keep loop report read-only" --why-this-next "auth blocks public release claims" --blocked-by "npm whoami E401" --owner-route deep --owner-scope "release readiness only" --scope-owner deep --side-effect "no publish attempted" --avoidance-note "do not retry publish before npm auth is proven" --issue-code "src/bin/yam.ts release report" --issue-role "summarizes release readiness without publishing" --issue-symptom "npm auth failure needs clearer next action" --changed-code "yam loop report" --changed-role "records loop evidence and learning note" --change-summary "added a read-only loop artifact" --why-important "it helps users learn what changed without overclaiming verification" --learning-note "fix blockers before claiming done" --json
152
152
  yam proof /path/to/project
153
153
  yam proof --route deep --truth verified --command "npm run build: pass"
154
154
  yam proof --route ueye --truth verified --visual "reference image only"
package/README.md CHANGED
@@ -142,7 +142,7 @@ yam ueye report --reference ./reference.png --actual .yam/screens/home.png --pre
142
142
  yam proof --route ueye --truth verified --visual "implementation screenshot evidence recorded" --design-completion '{"completion_claim":"done","has_implementation_screenshot":true,"design_quality":"pass","states_checked":true,"mobile_checked":true,"contrast_checked":true,"cta_checked":true,"direction_locked":true,"truth_status":"verified"}'
143
143
  yam media proof --requested --attempted --output ./generated.png --wait-loop --json
144
144
  yam proof --route mission --mission-envelope '{"agent_id":"implementer","assigned_scope":"target component","changed_files":["src/file.ts"],"verification_hint":"npm run typecheck","truth_status":"partial"}'
145
- yam loop report --route quick --intent "fix release readiness" --stage "inspect:passed:read release report" --evidence "typecheck passed" --evidence-level local --evidence-stamp "sha256:release-report" --blocked-kind auth_blocked --safe-retry "retry after npm whoami succeeds" --fix-first-item "npm auth must be verified before publish" --remaining-task "rerun release report after auth refresh" --recommended-direction "fix npm auth first, then publish manually" --implementation-note "keep loop report read-only" --why-this-next "auth blocks public release claims" --blocked-by "npm whoami E401" --owner-route deep --owner-scope "release readiness only" --scope-owner deep --side-effect "no publish attempted" --issue-code "src/bin/yam.ts release report" --issue-role "summarizes release readiness without publishing" --issue-symptom "npm auth failure needs clearer next action" --changed-code "yam loop report" --changed-role "records loop evidence and learning note" --change-summary "added a read-only loop artifact" --why-important "it helps users learn what changed without overclaiming verification" --learning-note "fix blockers before claiming done" --json
145
+ yam loop report --route quick --intent "fix release readiness" --stage "inspect:passed:read release report" --evidence "typecheck passed" --evidence-level local --evidence-stamp "sha256:release-report" --covered-requirement "release report is read-only" --uncovered-requirement "npm auth verified" --blocked-kind auth_blocked --failure-cause auth_token_invalid --safe-retry "retry after npm whoami succeeds" --recovery-hint "refresh npm auth, then rerun readiness checks" --fix-first-item "npm auth must be verified before publish" --remaining-task "rerun release report after auth refresh" --recommended-direction "fix npm auth first, then publish manually" --implementation-note "keep loop report read-only" --why-this-next "auth blocks public release claims" --blocked-by "npm whoami E401" --owner-route deep --owner-scope "release readiness only" --scope-owner deep --side-effect "no publish attempted" --avoidance-note "do not retry publish before npm auth is proven" --issue-code "src/bin/yam.ts release report" --issue-role "summarizes release readiness without publishing" --issue-symptom "npm auth failure needs clearer next action" --changed-code "yam loop report" --changed-role "records loop evidence and learning note" --change-summary "added a read-only loop artifact" --why-important "it helps users learn what changed without overclaiming verification" --learning-note "fix blockers before claiming done" --json
146
146
  yam release report --json
147
147
  yam safety "supabase db reset"
148
148
  yam detect /path/to/project
@@ -215,11 +215,12 @@ These shapes keep reports machine-readable without turning normal work into a he
215
215
 
216
216
  - Runtime evidence mini: compact route, command/process, observation, cleanup, truth status, and next action fields for `$deep` or `$mission` runtime claims.
217
217
  - Patch queue lite: `$mission` lane records with pending/applied/verified/reverted/blocked state, changed files, verification hint, rollback hint, truth status, and next action.
218
- - Release report JSON: `yam release report --json` summarizes typecheck, forbidden-name scan, package boundary, registry status, CLI smoke, dist freshness, diagnostics, and final truth status.
219
- - Loop report JSON: `yam loop report --json` records a read-only handoff artifact: guided stages, evidence level/stamp, blocker kind, safe retry, owner route/scope, side effects, next action, fix-first items, remaining tasks, recommended direction, implementation notes, blocked-by notes, tool intent, truth status, and study note.
218
+ - Release report JSON: `yam release report --json` summarizes typecheck, forbidden-name scan, package boundary, registry status, CLI smoke, dist freshness, diagnostics, readiness receipt, and final truth status.
219
+ - Loop report JSON: `yam loop report --json` records a read-only handoff artifact: guided stages, requirement coverage, evidence level/stamp, blocker kind, failure cause, safe retry, recovery hint, owner route/scope, side effects, next action, fix-first items, remaining tasks, recommended direction, implementation notes, blocked-by notes, avoidance note, tool intent, truth status, and study note.
220
220
  - Study note: `yam.study-note.v1` keeps short non-specialist explanations for what code was failing, what role it plays, how the symptom showed up, what changed, and why it matters. Missing details stay in `limits` instead of being guessed.
221
+ - Avoidance note: loop reports may include one short mistake-to-avoid note, but durable memory stays explicit through `yam memory add`; `yam loop report` does not write `.yam/memory`.
221
222
  - Publish blocker evidence: release reports classify common npm auth, permission, OTP, immutable-version, cache, registry, and tarball failures into safe next actions.
222
- - Publish readiness evidence: release reports run read-only registry/auth/version probes, redact account/token details, and keep npm publish outside the report.
223
+ - Publish readiness evidence: release reports run read-only registry/auth/version probes, redact account/token details, keep npm publish outside the report, and include a `yam.release-readiness-receipt.v1` basis for the readiness judgment.
223
224
  - Context pressure: `yam context pressure` explains when project context is getting stale, broad, or confusing enough to summarize, refresh, narrow scope, or deepen route.
224
225
  - Cleanup scan: `yam cleanup scan` is read-only and advisory; it reports confusing hooks, local skill folders, stale traces, or old proof artifacts without deleting anything.
225
226
  - Benchmark optimization loop lite: use `yam measure <route>` and `yam template tuning` to record baseline, one route wording change, rerun result, keep/revert decision, and stop condition.
package/dist/bin/yam.js CHANGED
@@ -1586,6 +1586,7 @@ function runReleaseReport() {
1586
1586
  const publishReadiness = releasePublishReadiness(checks, provenance, tarball, publishBlockerEvidence);
1587
1587
  const nextActions = releaseNextActions(checks, provenance, tarball, publishBlockerEvidence, publishReadiness);
1588
1588
  const ok = failed.length === 0 && publishReadiness.status === 'ready';
1589
+ const readinessReceipt = releaseReadinessReceipt(provenance, freshness, tarball, publishReadiness, ok);
1589
1590
  return {
1590
1591
  schema: 'yam.release-report.v1',
1591
1592
  generated_at: startedAt,
@@ -1599,11 +1600,53 @@ function runReleaseReport() {
1599
1600
  tarball,
1600
1601
  freshness,
1601
1602
  publish_readiness: publishReadiness,
1603
+ readiness_receipt: readinessReceipt,
1602
1604
  study_note: releaseStudyNote(publishReadiness, publishBlockerEvidence),
1603
1605
  publishBlockerEvidence,
1604
1606
  next_actions: nextActions
1605
1607
  };
1606
1608
  }
1609
+ function releaseReadinessReceipt(provenance = {}, freshness = {}, tarball = {}, publishReadiness = {}, ok = false) {
1610
+ return {
1611
+ schema: 'yam.release-readiness-receipt.v1',
1612
+ generated_at: new Date().toISOString(),
1613
+ package_name: PACKAGE_JSON.name,
1614
+ local_version: VERSION,
1615
+ registry: {
1616
+ url: String(publishReadiness.registry?.url || ''),
1617
+ latest_version: String(publishReadiness.registry?.latest_version || ''),
1618
+ version_status: String(publishReadiness.registry?.version_status || 'not_verified'),
1619
+ truth_status: publishReadiness.registry?.truth_status || 'partial'
1620
+ },
1621
+ auth: {
1622
+ command: 'npm whoami',
1623
+ status: String(publishReadiness.auth?.status || 'not_verified'),
1624
+ account: publishReadiness.auth?.status === 'authenticated' ? 'observed_redacted' : '',
1625
+ truth_status: publishReadiness.auth?.truth_status || 'partial'
1626
+ },
1627
+ git: {
1628
+ commit: String(provenance.git_commit || ''),
1629
+ dirty: Boolean(provenance.git_dirty),
1630
+ dirty_lines_observed: Array.isArray(provenance.git_status_lines) ? provenance.git_status_lines.length : 0,
1631
+ truth_status: provenance.truth_status || 'partial'
1632
+ },
1633
+ checks: {
1634
+ dist: freshness.dist || 'not-verified',
1635
+ package_boundary: freshness.package_boundary || 'not-verified',
1636
+ cli_smoke: freshness.cli_smoke || 'not-verified',
1637
+ tarball: tarball.status === 'checked' ? 'checked' : 'not-verified'
1638
+ },
1639
+ tarball: {
1640
+ status: String(tarball.status || 'not-verified'),
1641
+ name: String(tarball.tarball_name || ''),
1642
+ integrity_present: Boolean(tarball.integrity),
1643
+ shasum_present: Boolean(tarball.shasum),
1644
+ truth_status: tarball.truth_status || 'partial'
1645
+ },
1646
+ publish_attempted: false,
1647
+ truth_status: ok ? 'verified' : 'blocked'
1648
+ };
1649
+ }
1607
1650
  function releaseProvenance(checks = []) {
1608
1651
  const gitCommit = runReadOnlyCommand('git', ['rev-parse', '--short', 'HEAD']);
1609
1652
  const gitStatus = runReadOnlyCommand('git', ['status', '--short']);
@@ -1979,13 +2022,13 @@ function loopUsage() {
1979
2022
  Read-only loop artifact helpers. A loop report records intent, stages, evidence, blockers, next action, handoff fields, and a short study note. It does not run agents, start processes, publish packages, or write files.
1980
2023
 
1981
2024
  Usage:
1982
- yam loop report [--route route] [--intent text] [--loop-kind harness|release|ueye|scout|deep|mission] [--stage id:status:note] [--evidence text] [--evidence-level none|fixture|smoke|local|real] [--evidence-stamp text] [--source-digest text] [--blocked text] [--blocked-kind text] [--safe-retry text] [--fix-first-item text] [--remaining-task text] [--recommended-direction text] [--implementation-note text] [--why-this-next text] [--blocked-by text] [--owner-route route] [--owner-scope text] [--scope-owner text] [--side-effect text] [--truth status] [--intent-label read_only|write|destructive|runtime|visual|publish] [--issue-code text] [--issue-role text] [--issue-symptom text] [--changed-code text] [--changed-role text] [--change-summary text] [--why-important text] [--learning-note text] [--json]
2025
+ yam loop report [--route route] [--intent text] [--loop-kind harness|release|ueye|scout|deep|mission] [--stage id:status:note] [--evidence text] [--evidence-level none|fixture|smoke|local|real] [--evidence-stamp text] [--source-digest text] [--covered-requirement text] [--uncovered-requirement text] [--blocked text] [--blocked-kind text] [--failure-cause text] [--safe-retry text] [--recovery-hint text] [--fix-first-item text] [--remaining-task text] [--recommended-direction text] [--implementation-note text] [--why-this-next text] [--blocked-by text] [--owner-route route] [--owner-scope text] [--scope-owner text] [--side-effect text] [--avoidance-note text] [--truth status] [--intent-label read_only|write|destructive|runtime|visual|publish] [--issue-code text] [--issue-role text] [--issue-symptom text] [--changed-code text] [--changed-role text] [--change-summary text] [--why-important text] [--learning-note text] [--json]
1983
2026
  `);
1984
2027
  }
1985
2028
  async function loopReport(args = []) {
1986
2029
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h')
1987
2030
  return loopUsage();
1988
- const flags = parseSimpleFlags(args, new Set(['route', 'intent', 'loop-kind', 'stage', 'stage-convention', 'evidence', 'evidence-level', 'evidence-stamp', 'source-digest', 'blocked', 'blocker', 'blocked-kind', 'next-action', 'safe-retry', 'fix-first-item', 'remaining-task', 'recommended-direction', 'direction', 'implementation-note', 'why-this-next', 'blocked-by', 'owner-route', 'owner-scope', 'scope-owner', 'owner', 'scope', 'side-effect', 'truth', 'intent-label', 'tool-intent', 'issue-code', 'issue-role', 'issue-symptom', 'changed-code', 'changed-role', 'change-summary', 'why-important', 'learning-note', 'json']));
2031
+ const flags = parseSimpleFlags(args, new Set(['route', 'intent', 'loop-kind', 'stage', 'stage-convention', 'evidence', 'evidence-level', 'evidence-stamp', 'source-digest', 'covered-requirement', 'uncovered-requirement', 'blocked', 'blocker', 'blocked-kind', 'failure-cause', 'next-action', 'safe-retry', 'recovery-hint', 'fix-first-item', 'remaining-task', 'recommended-direction', 'direction', 'implementation-note', 'why-this-next', 'blocked-by', 'owner-route', 'owner-scope', 'scope-owner', 'owner', 'scope', 'side-effect', 'avoidance-note', 'truth', 'intent-label', 'tool-intent', 'issue-code', 'issue-role', 'issue-symptom', 'changed-code', 'changed-role', 'change-summary', 'why-important', 'learning-note', 'json']));
1989
2032
  const blockers = [...arrayFlag(flags.blocked), ...arrayFlag(flags.blocker)];
1990
2033
  const report = buildLoopReport({
1991
2034
  route: normalizeRoute(flags.route) || String(flags.route || ''),
@@ -1997,12 +2040,16 @@ async function loopReport(args = []) {
1997
2040
  evidence_level: normalizeLoopEvidenceLevelFlag(flags.evidence_level),
1998
2041
  evidence_stamp: String(flags.evidence_stamp || ''),
1999
2042
  source_digest: String(flags.source_digest || ''),
2043
+ covered_requirements: arrayFlag(flags.covered_requirement),
2044
+ uncovered_requirements: arrayFlag(flags.uncovered_requirement),
2000
2045
  blockers,
2001
2046
  blocked_kind: String(flags.blocked_kind || ''),
2047
+ failure_cause: String(flags.failure_cause || ''),
2002
2048
  truth_status: isTruthStatus(flags.truth) ? flags.truth : undefined,
2003
2049
  intent_label: normalizeToolIntent(flags.intent_label || flags.tool_intent || 'read_only'),
2004
2050
  next_action: String(flags.next_action || ''),
2005
2051
  safe_retry: String(flags.safe_retry || ''),
2052
+ recovery_hint: String(flags.recovery_hint || ''),
2006
2053
  fix_first_items: arrayFlag(flags.fix_first_item),
2007
2054
  remaining_tasks: arrayFlag(flags.remaining_task),
2008
2055
  recommended_direction: String(flags.recommended_direction || flags.direction || ''),
@@ -2013,6 +2060,7 @@ async function loopReport(args = []) {
2013
2060
  owner_scope: [...arrayFlag(flags.owner_scope), ...arrayFlag(flags.scope)],
2014
2061
  scope_owner: String(flags.scope_owner || flags.owner || ''),
2015
2062
  side_effects: arrayFlag(flags.side_effect),
2063
+ avoidance_note: String(flags.avoidance_note || ''),
2016
2064
  issue_code: String(flags.issue_code || ''),
2017
2065
  issue_role: String(flags.issue_role || ''),
2018
2066
  issue_symptom: String(flags.issue_symptom || ''),
@@ -161,13 +161,17 @@ export interface LoopReport {
161
161
  evidence_level: LoopEvidenceLevel;
162
162
  evidence_stamp: string;
163
163
  source_digest: string;
164
+ covered_requirements: string[];
165
+ uncovered_requirements: string[];
164
166
  blockers: string[];
165
167
  blocked_kind: string;
168
+ failure_cause: string;
166
169
  truth_status: TruthStatus;
167
170
  intent_label: ToolIntent;
168
171
  tool_intent: ToolIntent;
169
172
  next_action: string;
170
173
  safe_retry: string;
174
+ recovery_hint: string;
171
175
  fix_first_items: string[];
172
176
  remaining_tasks: string[];
173
177
  recommended_direction: string;
@@ -178,6 +182,7 @@ export interface LoopReport {
178
182
  owner_scope: string[];
179
183
  scope_owner: string;
180
184
  side_effects: string[];
185
+ avoidance_note: string;
181
186
  study_note: StudyNote;
182
187
  }
183
188
  export interface UeyeRunReport {
@@ -236,13 +236,18 @@ export function buildLoopReport(input = {}) {
236
236
  ? input.stages.map((stage) => buildLoopStage(stage))
237
237
  : [];
238
238
  const evidence = asList(input.evidence);
239
+ const coveredRequirements = asList(input.covered_requirements || input.covered_requirement);
240
+ const uncoveredRequirements = asList(input.uncovered_requirements || input.uncovered_requirement);
239
241
  const blockers = asList(input.blockers || input.blocked);
240
242
  const blockedBy = asList(input.blocked_by);
241
- const blockedKind = shortText(input.blocked_kind || (blockers.length || blockedBy.length ? 'evidence_missing' : ''));
243
+ const blockedKind = shortText(input.blocked_kind || (uncoveredRequirements.length ? 'requirement_uncovered' : blockers.length || blockedBy.length ? 'evidence_missing' : ''));
244
+ const failureCause = shortText(input.failure_cause);
245
+ const recoveryHint = shortText(input.recovery_hint);
246
+ const avoidanceNote = shortText(input.avoidance_note);
242
247
  const evidenceStamp = shortText(input.evidence_stamp || input.source_digest, 320);
243
248
  const sourceDigest = shortText(input.source_digest || input.evidence_stamp, 320);
244
249
  const hasBlockedStage = stages.some((stage) => stage.status === 'blocked' || stage.status === 'failed');
245
- const hasBlockerSignal = Boolean(blockers.length || blockedBy.length || blockedKind);
250
+ const hasBlockerSignal = Boolean(blockers.length || blockedBy.length || blockedKind || uncoveredRequirements.length);
246
251
  const requestedTruth = isTruthStatus(input.truth_status) ? input.truth_status : '';
247
252
  const derivedTruth = hasBlockerSignal || hasBlockedStage ? 'blocked'
248
253
  : evidence.length && stages.some((stage) => stage.status === 'passed') ? 'verified'
@@ -265,13 +270,17 @@ export function buildLoopReport(input = {}) {
265
270
  evidence_level: normalizeLoopEvidenceLevel(input.evidence_level),
266
271
  evidence_stamp: evidenceStamp,
267
272
  source_digest: sourceDigest,
273
+ covered_requirements: coveredRequirements,
274
+ uncovered_requirements: uncoveredRequirements,
268
275
  blockers,
269
276
  blocked_kind: blockedKind,
277
+ failure_cause: failureCause,
270
278
  truth_status: truth,
271
279
  intent_label: intentLabel,
272
280
  tool_intent: intentLabel,
273
- next_action: shortText(input.next_action || (hasBlockerSignal ? 'resolve the blocker before claiming this loop complete' : 'record the next smallest useful action')),
281
+ next_action: shortText(input.next_action || (uncoveredRequirements.length ? `cover requirement before claiming complete: ${uncoveredRequirements[0]}` : hasBlockerSignal ? 'resolve the blocker before claiming this loop complete' : 'record the next smallest useful action')),
274
282
  safe_retry: shortText(input.safe_retry || (hasBlockerSignal ? 'retry only after the blocker is resolved and new evidence is recorded' : 'not required')),
283
+ recovery_hint: recoveryHint,
275
284
  fix_first_items: asList(input.fix_first_items || input.fix_first_item),
276
285
  remaining_tasks: asList(input.remaining_tasks || input.remaining_task),
277
286
  recommended_direction: shortText(input.recommended_direction || input.direction),
@@ -282,6 +291,7 @@ export function buildLoopReport(input = {}) {
282
291
  owner_scope: asList(input.owner_scope || input.scope),
283
292
  scope_owner: normalizeOwnerRoute(input.scope_owner || input.owner),
284
293
  side_effects: asList(input.side_effects || input.side_effect),
294
+ avoidance_note: avoidanceNote,
285
295
  study_note: studyNote
286
296
  };
287
297
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yam-flow",
3
- "version": "1.9.1",
3
+ "version": "2.0.0",
4
4
  "description": "Progressive proof-first agent flow: start fast, deepen deliberately, stay honest by design.",
5
5
  "type": "module",
6
6
  "author": "0kim0bos",
@@ -77,11 +77,13 @@ Use this when the next run needs a clear starting point:
77
77
  - Recommended direction.
78
78
  - Why this next step matters.
79
79
  - What is blocked, and by what.
80
+ - Covered and uncovered requirements when the next run could otherwise overclaim completion.
80
81
  - Evidence level or stamp when the handoff relies on a specific command, file, or report.
81
- - Blocker kind and safe retry when the next run should not blindly repeat the same action.
82
+ - Blocker kind, failure cause, recovery hint, and safe retry when the next run should not blindly repeat the same action.
83
+ - Avoidance note when a repeated mistake should be easy to spot; durable memory still uses explicit `yam memory add`.
82
84
  - Owner route, scope owner, and side effects when more than one person or route may continue the work.
83
85
 
84
- `yam loop report` can record this as a handoff artifact with `fix_first_items`, `remaining_tasks`, `recommended_direction`, `implementation_notes`, `why_this_next`, `blocked_by`, `blocked_kind`, `safe_retry`, `evidence_level`, `evidence_stamp`, `owner_route`, `owner_scope`, `scope_owner`, and `side_effects`.
86
+ `yam loop report` can record this as a handoff artifact with `fix_first_items`, `remaining_tasks`, `recommended_direction`, `implementation_notes`, `why_this_next`, `blocked_by`, `covered_requirements`, `uncovered_requirements`, `blocked_kind`, `failure_cause`, `recovery_hint`, `safe_retry`, `evidence_level`, `evidence_stamp`, `owner_route`, `owner_scope`, `scope_owner`, `side_effects`, and `avoidance_note`.
85
87
 
86
88
  ## Route Weight
87
89
 
package/src/bin/yam.ts CHANGED
@@ -1575,6 +1575,7 @@ function runReleaseReport() {
1575
1575
  const publishReadiness = releasePublishReadiness(checks, provenance, tarball, publishBlockerEvidence);
1576
1576
  const nextActions = releaseNextActions(checks, provenance, tarball, publishBlockerEvidence, publishReadiness);
1577
1577
  const ok = failed.length === 0 && publishReadiness.status === 'ready';
1578
+ const readinessReceipt = releaseReadinessReceipt(provenance, freshness, tarball, publishReadiness, ok);
1578
1579
  return {
1579
1580
  schema: 'yam.release-report.v1',
1580
1581
  generated_at: startedAt,
@@ -1588,12 +1589,55 @@ function runReleaseReport() {
1588
1589
  tarball,
1589
1590
  freshness,
1590
1591
  publish_readiness: publishReadiness,
1592
+ readiness_receipt: readinessReceipt,
1591
1593
  study_note: releaseStudyNote(publishReadiness, publishBlockerEvidence),
1592
1594
  publishBlockerEvidence,
1593
1595
  next_actions: nextActions
1594
1596
  };
1595
1597
  }
1596
1598
 
1599
+ function releaseReadinessReceipt(provenance: AnyRecord = {}, freshness: AnyRecord = {}, tarball: AnyRecord = {}, publishReadiness: AnyRecord = {}, ok = false) {
1600
+ return {
1601
+ schema: 'yam.release-readiness-receipt.v1',
1602
+ generated_at: new Date().toISOString(),
1603
+ package_name: PACKAGE_JSON.name,
1604
+ local_version: VERSION,
1605
+ registry: {
1606
+ url: String(publishReadiness.registry?.url || ''),
1607
+ latest_version: String(publishReadiness.registry?.latest_version || ''),
1608
+ version_status: String(publishReadiness.registry?.version_status || 'not_verified'),
1609
+ truth_status: publishReadiness.registry?.truth_status || 'partial'
1610
+ },
1611
+ auth: {
1612
+ command: 'npm whoami',
1613
+ status: String(publishReadiness.auth?.status || 'not_verified'),
1614
+ account: publishReadiness.auth?.status === 'authenticated' ? 'observed_redacted' : '',
1615
+ truth_status: publishReadiness.auth?.truth_status || 'partial'
1616
+ },
1617
+ git: {
1618
+ commit: String(provenance.git_commit || ''),
1619
+ dirty: Boolean(provenance.git_dirty),
1620
+ dirty_lines_observed: Array.isArray(provenance.git_status_lines) ? provenance.git_status_lines.length : 0,
1621
+ truth_status: provenance.truth_status || 'partial'
1622
+ },
1623
+ checks: {
1624
+ dist: freshness.dist || 'not-verified',
1625
+ package_boundary: freshness.package_boundary || 'not-verified',
1626
+ cli_smoke: freshness.cli_smoke || 'not-verified',
1627
+ tarball: tarball.status === 'checked' ? 'checked' : 'not-verified'
1628
+ },
1629
+ tarball: {
1630
+ status: String(tarball.status || 'not-verified'),
1631
+ name: String(tarball.tarball_name || ''),
1632
+ integrity_present: Boolean(tarball.integrity),
1633
+ shasum_present: Boolean(tarball.shasum),
1634
+ truth_status: tarball.truth_status || 'partial'
1635
+ },
1636
+ publish_attempted: false,
1637
+ truth_status: ok ? 'verified' : 'blocked'
1638
+ };
1639
+ }
1640
+
1597
1641
  function releaseProvenance(checks = []) {
1598
1642
  const gitCommit = runReadOnlyCommand('git', ['rev-parse', '--short', 'HEAD']);
1599
1643
  const gitStatus = runReadOnlyCommand('git', ['status', '--short']);
@@ -1977,13 +2021,13 @@ function loopUsage() {
1977
2021
  Read-only loop artifact helpers. A loop report records intent, stages, evidence, blockers, next action, handoff fields, and a short study note. It does not run agents, start processes, publish packages, or write files.
1978
2022
 
1979
2023
  Usage:
1980
- yam loop report [--route route] [--intent text] [--loop-kind harness|release|ueye|scout|deep|mission] [--stage id:status:note] [--evidence text] [--evidence-level none|fixture|smoke|local|real] [--evidence-stamp text] [--source-digest text] [--blocked text] [--blocked-kind text] [--safe-retry text] [--fix-first-item text] [--remaining-task text] [--recommended-direction text] [--implementation-note text] [--why-this-next text] [--blocked-by text] [--owner-route route] [--owner-scope text] [--scope-owner text] [--side-effect text] [--truth status] [--intent-label read_only|write|destructive|runtime|visual|publish] [--issue-code text] [--issue-role text] [--issue-symptom text] [--changed-code text] [--changed-role text] [--change-summary text] [--why-important text] [--learning-note text] [--json]
2024
+ yam loop report [--route route] [--intent text] [--loop-kind harness|release|ueye|scout|deep|mission] [--stage id:status:note] [--evidence text] [--evidence-level none|fixture|smoke|local|real] [--evidence-stamp text] [--source-digest text] [--covered-requirement text] [--uncovered-requirement text] [--blocked text] [--blocked-kind text] [--failure-cause text] [--safe-retry text] [--recovery-hint text] [--fix-first-item text] [--remaining-task text] [--recommended-direction text] [--implementation-note text] [--why-this-next text] [--blocked-by text] [--owner-route route] [--owner-scope text] [--scope-owner text] [--side-effect text] [--avoidance-note text] [--truth status] [--intent-label read_only|write|destructive|runtime|visual|publish] [--issue-code text] [--issue-role text] [--issue-symptom text] [--changed-code text] [--changed-role text] [--change-summary text] [--why-important text] [--learning-note text] [--json]
1981
2025
  `);
1982
2026
  }
1983
2027
 
1984
2028
  async function loopReport(args = []) {
1985
2029
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return loopUsage();
1986
- const flags = parseSimpleFlags(args, new Set(['route', 'intent', 'loop-kind', 'stage', 'stage-convention', 'evidence', 'evidence-level', 'evidence-stamp', 'source-digest', 'blocked', 'blocker', 'blocked-kind', 'next-action', 'safe-retry', 'fix-first-item', 'remaining-task', 'recommended-direction', 'direction', 'implementation-note', 'why-this-next', 'blocked-by', 'owner-route', 'owner-scope', 'scope-owner', 'owner', 'scope', 'side-effect', 'truth', 'intent-label', 'tool-intent', 'issue-code', 'issue-role', 'issue-symptom', 'changed-code', 'changed-role', 'change-summary', 'why-important', 'learning-note', 'json']));
2030
+ const flags = parseSimpleFlags(args, new Set(['route', 'intent', 'loop-kind', 'stage', 'stage-convention', 'evidence', 'evidence-level', 'evidence-stamp', 'source-digest', 'covered-requirement', 'uncovered-requirement', 'blocked', 'blocker', 'blocked-kind', 'failure-cause', 'next-action', 'safe-retry', 'recovery-hint', 'fix-first-item', 'remaining-task', 'recommended-direction', 'direction', 'implementation-note', 'why-this-next', 'blocked-by', 'owner-route', 'owner-scope', 'scope-owner', 'owner', 'scope', 'side-effect', 'avoidance-note', 'truth', 'intent-label', 'tool-intent', 'issue-code', 'issue-role', 'issue-symptom', 'changed-code', 'changed-role', 'change-summary', 'why-important', 'learning-note', 'json']));
1987
2031
  const blockers = [...arrayFlag(flags.blocked), ...arrayFlag(flags.blocker)];
1988
2032
  const report = buildLoopReport({
1989
2033
  route: normalizeRoute(flags.route) || String(flags.route || ''),
@@ -1995,12 +2039,16 @@ async function loopReport(args = []) {
1995
2039
  evidence_level: normalizeLoopEvidenceLevelFlag(flags.evidence_level),
1996
2040
  evidence_stamp: String(flags.evidence_stamp || ''),
1997
2041
  source_digest: String(flags.source_digest || ''),
2042
+ covered_requirements: arrayFlag(flags.covered_requirement),
2043
+ uncovered_requirements: arrayFlag(flags.uncovered_requirement),
1998
2044
  blockers,
1999
2045
  blocked_kind: String(flags.blocked_kind || ''),
2046
+ failure_cause: String(flags.failure_cause || ''),
2000
2047
  truth_status: isTruthStatus(flags.truth) ? flags.truth : undefined,
2001
2048
  intent_label: normalizeToolIntent(flags.intent_label || flags.tool_intent || 'read_only'),
2002
2049
  next_action: String(flags.next_action || ''),
2003
2050
  safe_retry: String(flags.safe_retry || ''),
2051
+ recovery_hint: String(flags.recovery_hint || ''),
2004
2052
  fix_first_items: arrayFlag(flags.fix_first_item),
2005
2053
  remaining_tasks: arrayFlag(flags.remaining_task),
2006
2054
  recommended_direction: String(flags.recommended_direction || flags.direction || ''),
@@ -2011,6 +2059,7 @@ async function loopReport(args = []) {
2011
2059
  owner_scope: [...arrayFlag(flags.owner_scope), ...arrayFlag(flags.scope)],
2012
2060
  scope_owner: String(flags.scope_owner || flags.owner || ''),
2013
2061
  side_effects: arrayFlag(flags.side_effect),
2062
+ avoidance_note: String(flags.avoidance_note || ''),
2014
2063
  issue_code: String(flags.issue_code || ''),
2015
2064
  issue_role: String(flags.issue_role || ''),
2016
2065
  issue_symptom: String(flags.issue_symptom || ''),
@@ -189,13 +189,17 @@ export interface LoopReport {
189
189
  evidence_level: LoopEvidenceLevel;
190
190
  evidence_stamp: string;
191
191
  source_digest: string;
192
+ covered_requirements: string[];
193
+ uncovered_requirements: string[];
192
194
  blockers: string[];
193
195
  blocked_kind: string;
196
+ failure_cause: string;
194
197
  truth_status: TruthStatus;
195
198
  intent_label: ToolIntent;
196
199
  tool_intent: ToolIntent;
197
200
  next_action: string;
198
201
  safe_retry: string;
202
+ recovery_hint: string;
199
203
  fix_first_items: string[];
200
204
  remaining_tasks: string[];
201
205
  recommended_direction: string;
@@ -206,6 +210,7 @@ export interface LoopReport {
206
210
  owner_scope: string[];
207
211
  scope_owner: string;
208
212
  side_effects: string[];
213
+ avoidance_note: string;
209
214
  study_note: StudyNote;
210
215
  }
211
216
 
@@ -550,13 +555,18 @@ export function buildLoopReport(input: Partial<LoopReport> & Record<string, unkn
550
555
  ? input.stages.map((stage) => buildLoopStage(stage))
551
556
  : [];
552
557
  const evidence = asList(input.evidence);
558
+ const coveredRequirements = asList(input.covered_requirements || input.covered_requirement);
559
+ const uncoveredRequirements = asList(input.uncovered_requirements || input.uncovered_requirement);
553
560
  const blockers = asList(input.blockers || input.blocked);
554
561
  const blockedBy = asList(input.blocked_by);
555
- const blockedKind = shortText(input.blocked_kind || (blockers.length || blockedBy.length ? 'evidence_missing' : ''));
562
+ const blockedKind = shortText(input.blocked_kind || (uncoveredRequirements.length ? 'requirement_uncovered' : blockers.length || blockedBy.length ? 'evidence_missing' : ''));
563
+ const failureCause = shortText(input.failure_cause);
564
+ const recoveryHint = shortText(input.recovery_hint);
565
+ const avoidanceNote = shortText(input.avoidance_note);
556
566
  const evidenceStamp = shortText(input.evidence_stamp || input.source_digest, 320);
557
567
  const sourceDigest = shortText(input.source_digest || input.evidence_stamp, 320);
558
568
  const hasBlockedStage = stages.some((stage) => stage.status === 'blocked' || stage.status === 'failed');
559
- const hasBlockerSignal = Boolean(blockers.length || blockedBy.length || blockedKind);
569
+ const hasBlockerSignal = Boolean(blockers.length || blockedBy.length || blockedKind || uncoveredRequirements.length);
560
570
  const requestedTruth = isTruthStatus(input.truth_status) ? input.truth_status : '';
561
571
  const derivedTruth: TruthStatus = hasBlockerSignal || hasBlockedStage ? 'blocked'
562
572
  : evidence.length && stages.some((stage) => stage.status === 'passed') ? 'verified'
@@ -579,13 +589,17 @@ export function buildLoopReport(input: Partial<LoopReport> & Record<string, unkn
579
589
  evidence_level: normalizeLoopEvidenceLevel(input.evidence_level),
580
590
  evidence_stamp: evidenceStamp,
581
591
  source_digest: sourceDigest,
592
+ covered_requirements: coveredRequirements,
593
+ uncovered_requirements: uncoveredRequirements,
582
594
  blockers,
583
595
  blocked_kind: blockedKind,
596
+ failure_cause: failureCause,
584
597
  truth_status: truth,
585
598
  intent_label: intentLabel,
586
599
  tool_intent: intentLabel,
587
- next_action: shortText(input.next_action || (hasBlockerSignal ? 'resolve the blocker before claiming this loop complete' : 'record the next smallest useful action')),
600
+ next_action: shortText(input.next_action || (uncoveredRequirements.length ? `cover requirement before claiming complete: ${uncoveredRequirements[0]}` : hasBlockerSignal ? 'resolve the blocker before claiming this loop complete' : 'record the next smallest useful action')),
588
601
  safe_retry: shortText(input.safe_retry || (hasBlockerSignal ? 'retry only after the blocker is resolved and new evidence is recorded' : 'not required')),
602
+ recovery_hint: recoveryHint,
589
603
  fix_first_items: asList(input.fix_first_items || input.fix_first_item),
590
604
  remaining_tasks: asList(input.remaining_tasks || input.remaining_task),
591
605
  recommended_direction: shortText(input.recommended_direction || input.direction),
@@ -596,6 +610,7 @@ export function buildLoopReport(input: Partial<LoopReport> & Record<string, unkn
596
610
  owner_scope: asList(input.owner_scope || input.scope),
597
611
  scope_owner: normalizeOwnerRoute(input.scope_owner || input.owner),
598
612
  side_effects: asList(input.side_effects || input.side_effect),
613
+ avoidance_note: avoidanceNote,
599
614
  study_note: studyNote
600
615
  };
601
616
  }
package/yam.manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yam",
3
- "version": "1.9.1",
3
+ "version": "2.0.0",
4
4
  "principles": [
5
5
  "Direction before execution.",
6
6
  "Start fast.",
@@ -12,7 +12,7 @@
12
12
  "Context-reuse first.",
13
13
  "End with remaining tasks and fix-first items.",
14
14
  "Explain what failed, what changed, and why it matters in short non-specialist language when useful.",
15
- "Use handoff notes for fix-first items, remaining tasks, recommended next direction, blocker kind, safe retry, evidence level, and owner scope when useful."
15
+ "Use handoff notes for fix-first items, remaining tasks, recommended next direction, requirement coverage, blocker kind, failure cause, recovery hint, safe retry, evidence level, and owner scope when useful."
16
16
  ],
17
17
  "defaultHooks": false,
18
18
  "routes": [