yam-flow 0.1.9 → 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,38 @@
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
+
26
+ ## v1.9.1 - 2026-06-19
27
+
28
+ ### Added
29
+
30
+ - Extended `yam loop report --json` with guided stage conventions, `evidence_level`, `evidence_stamp`/`source_digest`, `blocked_kind`, `safe_retry`, `owner_scope`, `scope_owner`, and `side_effects` so the report can act as a clearer handoff artifact without adding a new command.
31
+ - Updated loop report smoke coverage to assert the new handoff fields.
32
+
33
+ ### Improved
34
+
35
+ - Updated README, COMMANDS, final-report guidance, implementation skill final-response rules, and manifest principles to prefer fix-first handoff with evidence level, blocker kind, safe retry, and owner/scope clarity.
36
+
5
37
  ## v0.1.9 - 2026-06-18
6
38
 
7
39
  ### 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" --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 --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"
@@ -195,7 +195,7 @@ Supported report shapes:
195
195
 
196
196
  - Runtime evidence mini: command/process, observation, cleanup, truth status, next action.
197
197
  - Patch queue lite: mission lane status, changed files, verification hint, rollback hint, truth status.
198
- - Loop report: intent, stage outcomes, evidence, blockers, next action, fix-first items, remaining tasks, recommended direction, blocked-by notes, owner route, and study note.
198
+ - Loop report: intent, guided stage outcomes, evidence level/stamp, blockers, blocker kind, safe retry, next action, fix-first items, remaining tasks, recommended direction, blocked-by notes, owner route/scope, side effects, and study note.
199
199
  - Study note: short non-specialist explanation of the failing code, its role, observed symptom, changed code, and why it matters.
200
200
  - Benchmark optimization loop lite: baseline, one change, rerun, keep/revert decision.
201
201
  - Structured diagnostic next action: severity, evidence, owner route, next action, truth status.
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" --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 --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: intent, stages, evidence, blockers, next action, fix-first items, remaining tasks, recommended direction, implementation notes, blocked-by notes, owner route, 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
@@ -524,6 +524,12 @@ function normalizeToolIntent(value = '') {
524
524
  return text;
525
525
  return 'read_only';
526
526
  }
527
+ function normalizeLoopEvidenceLevelFlag(value = '') {
528
+ const text = String(value || '').toLowerCase().replace(/[-\s]/g, '_');
529
+ if (['fixture', 'smoke', 'local', 'real'].includes(text))
530
+ return text;
531
+ return 'none';
532
+ }
527
533
  function uniqueNextActionDetails(actions = []) {
528
534
  const seen = new Set();
529
535
  const unique = [];
@@ -1580,6 +1586,7 @@ function runReleaseReport() {
1580
1586
  const publishReadiness = releasePublishReadiness(checks, provenance, tarball, publishBlockerEvidence);
1581
1587
  const nextActions = releaseNextActions(checks, provenance, tarball, publishBlockerEvidence, publishReadiness);
1582
1588
  const ok = failed.length === 0 && publishReadiness.status === 'ready';
1589
+ const readinessReceipt = releaseReadinessReceipt(provenance, freshness, tarball, publishReadiness, ok);
1583
1590
  return {
1584
1591
  schema: 'yam.release-report.v1',
1585
1592
  generated_at: startedAt,
@@ -1593,11 +1600,53 @@ function runReleaseReport() {
1593
1600
  tarball,
1594
1601
  freshness,
1595
1602
  publish_readiness: publishReadiness,
1603
+ readiness_receipt: readinessReceipt,
1596
1604
  study_note: releaseStudyNote(publishReadiness, publishBlockerEvidence),
1597
1605
  publishBlockerEvidence,
1598
1606
  next_actions: nextActions
1599
1607
  };
1600
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
+ }
1601
1650
  function releaseProvenance(checks = []) {
1602
1651
  const gitCommit = runReadOnlyCommand('git', ['rev-parse', '--short', 'HEAD']);
1603
1652
  const gitStatus = runReadOnlyCommand('git', ['status', '--short']);
@@ -1973,24 +2022,34 @@ function loopUsage() {
1973
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.
1974
2023
 
1975
2024
  Usage:
1976
- yam loop report [--route route] [--intent text] [--loop-kind harness|release|ueye|scout|deep|mission] [--stage id:status:note] [--evidence text] [--blocked text] [--fix-first-item text] [--remaining-task text] [--recommended-direction text] [--implementation-note text] [--why-this-next text] [--blocked-by text] [--owner-route route] [--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]
1977
2026
  `);
1978
2027
  }
1979
2028
  async function loopReport(args = []) {
1980
2029
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h')
1981
2030
  return loopUsage();
1982
- const flags = parseSimpleFlags(args, new Set(['route', 'intent', 'loop-kind', 'stage', 'evidence', 'blocked', 'blocker', 'next-action', 'fix-first-item', 'remaining-task', 'recommended-direction', 'direction', 'implementation-note', 'why-this-next', 'blocked-by', 'owner-route', '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']));
1983
2032
  const blockers = [...arrayFlag(flags.blocked), ...arrayFlag(flags.blocker)];
1984
2033
  const report = buildLoopReport({
1985
2034
  route: normalizeRoute(flags.route) || String(flags.route || ''),
1986
2035
  intent: String(flags.intent || ''),
1987
2036
  loop_kind: String(flags.loop_kind || flags.route || 'harness'),
2037
+ stage_conventions: arrayFlag(flags.stage_convention),
1988
2038
  stages: arrayFlag(flags.stage),
1989
2039
  evidence: arrayFlag(flags.evidence),
2040
+ evidence_level: normalizeLoopEvidenceLevelFlag(flags.evidence_level),
2041
+ evidence_stamp: String(flags.evidence_stamp || ''),
2042
+ source_digest: String(flags.source_digest || ''),
2043
+ covered_requirements: arrayFlag(flags.covered_requirement),
2044
+ uncovered_requirements: arrayFlag(flags.uncovered_requirement),
1990
2045
  blockers,
2046
+ blocked_kind: String(flags.blocked_kind || ''),
2047
+ failure_cause: String(flags.failure_cause || ''),
1991
2048
  truth_status: isTruthStatus(flags.truth) ? flags.truth : undefined,
1992
2049
  intent_label: normalizeToolIntent(flags.intent_label || flags.tool_intent || 'read_only'),
1993
2050
  next_action: String(flags.next_action || ''),
2051
+ safe_retry: String(flags.safe_retry || ''),
2052
+ recovery_hint: String(flags.recovery_hint || ''),
1994
2053
  fix_first_items: arrayFlag(flags.fix_first_item),
1995
2054
  remaining_tasks: arrayFlag(flags.remaining_task),
1996
2055
  recommended_direction: String(flags.recommended_direction || flags.direction || ''),
@@ -1998,6 +2057,10 @@ async function loopReport(args = []) {
1998
2057
  why_this_next: String(flags.why_this_next || ''),
1999
2058
  blocked_by: arrayFlag(flags.blocked_by),
2000
2059
  owner_route: String(flags.owner_route || flags.route || ''),
2060
+ owner_scope: [...arrayFlag(flags.owner_scope), ...arrayFlag(flags.scope)],
2061
+ scope_owner: String(flags.scope_owner || flags.owner || ''),
2062
+ side_effects: arrayFlag(flags.side_effect),
2063
+ avoidance_note: String(flags.avoidance_note || ''),
2001
2064
  issue_code: String(flags.issue_code || ''),
2002
2065
  issue_role: String(flags.issue_role || ''),
2003
2066
  issue_symptom: String(flags.issue_symptom || ''),
@@ -6,6 +6,7 @@ export type RuntimeSubsystem = 'real_runtime' | 'tmux_physical' | 'process_clean
6
6
  export type RuntimeBackend = 'none' | 'in_app_browser' | 'playwright' | 'terminal' | 'tmux' | 'zellij' | 'unknown';
7
7
  export type RuntimeClaim = 'not_started' | 'started' | 'observed' | 'stopped' | 'cleanup_verified' | 'blocked' | 'unknown';
8
8
  export type LoopStageStatus = 'passed' | 'failed' | 'blocked' | 'skipped' | 'partial' | 'pending' | 'recorded';
9
+ export type LoopEvidenceLevel = 'none' | 'fixture' | 'smoke' | 'local' | 'real';
9
10
  export type ToolIntent = 'read_only' | 'write' | 'destructive' | 'runtime' | 'visual' | 'publish';
10
11
  export interface SafetyHit {
11
12
  level: SafetyLevel;
@@ -154,13 +155,23 @@ export interface LoopReport {
154
155
  route: string;
155
156
  intent: string;
156
157
  loop_kind: string;
158
+ stage_conventions: string[];
157
159
  stages: LoopStage[];
158
160
  evidence: string[];
161
+ evidence_level: LoopEvidenceLevel;
162
+ evidence_stamp: string;
163
+ source_digest: string;
164
+ covered_requirements: string[];
165
+ uncovered_requirements: string[];
159
166
  blockers: string[];
167
+ blocked_kind: string;
168
+ failure_cause: string;
160
169
  truth_status: TruthStatus;
161
170
  intent_label: ToolIntent;
162
171
  tool_intent: ToolIntent;
163
172
  next_action: string;
173
+ safe_retry: string;
174
+ recovery_hint: string;
164
175
  fix_first_items: string[];
165
176
  remaining_tasks: string[];
166
177
  recommended_direction: string;
@@ -168,6 +179,10 @@ export interface LoopReport {
168
179
  why_this_next: string;
169
180
  blocked_by: string[];
170
181
  owner_route: string;
182
+ owner_scope: string[];
183
+ scope_owner: string;
184
+ side_effects: string[];
185
+ avoidance_note: string;
171
186
  study_note: StudyNote;
172
187
  }
173
188
  export interface UeyeRunReport {
@@ -236,10 +236,20 @@ 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);
242
+ const blockedBy = asList(input.blocked_by);
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);
247
+ const evidenceStamp = shortText(input.evidence_stamp || input.source_digest, 320);
248
+ const sourceDigest = shortText(input.source_digest || input.evidence_stamp, 320);
240
249
  const hasBlockedStage = stages.some((stage) => stage.status === 'blocked' || stage.status === 'failed');
250
+ const hasBlockerSignal = Boolean(blockers.length || blockedBy.length || blockedKind || uncoveredRequirements.length);
241
251
  const requestedTruth = isTruthStatus(input.truth_status) ? input.truth_status : '';
242
- const derivedTruth = blockers.length || hasBlockedStage ? 'blocked'
252
+ const derivedTruth = hasBlockerSignal || hasBlockedStage ? 'blocked'
243
253
  : evidence.length && stages.some((stage) => stage.status === 'passed') ? 'verified'
244
254
  : evidence.length || stages.length ? 'partial'
245
255
  : 'assumed';
@@ -252,20 +262,36 @@ export function buildLoopReport(input = {}) {
252
262
  route: String(input.route || ''),
253
263
  intent: shortText(input.intent),
254
264
  loop_kind: String(input.loop_kind || 'harness'),
265
+ stage_conventions: asList(input.stage_conventions || input.stage_convention).length
266
+ ? asList(input.stage_conventions || input.stage_convention)
267
+ : ['brief', 'plan', 'implement', 'critique', 'verify', 'handoff'],
255
268
  stages,
256
269
  evidence,
270
+ evidence_level: normalizeLoopEvidenceLevel(input.evidence_level),
271
+ evidence_stamp: evidenceStamp,
272
+ source_digest: sourceDigest,
273
+ covered_requirements: coveredRequirements,
274
+ uncovered_requirements: uncoveredRequirements,
257
275
  blockers,
276
+ blocked_kind: blockedKind,
277
+ failure_cause: failureCause,
258
278
  truth_status: truth,
259
279
  intent_label: intentLabel,
260
280
  tool_intent: intentLabel,
261
- next_action: shortText(input.next_action || (blockers[0] ? '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')),
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,
262
284
  fix_first_items: asList(input.fix_first_items || input.fix_first_item),
263
285
  remaining_tasks: asList(input.remaining_tasks || input.remaining_task),
264
286
  recommended_direction: shortText(input.recommended_direction || input.direction),
265
287
  implementation_notes: asList(input.implementation_notes || input.implementation_note),
266
288
  why_this_next: shortText(input.why_this_next),
267
- blocked_by: asList(input.blocked_by),
289
+ blocked_by: blockedBy,
268
290
  owner_route: normalizeOwnerRoute(input.owner_route || input.route),
291
+ owner_scope: asList(input.owner_scope || input.scope),
292
+ scope_owner: normalizeOwnerRoute(input.scope_owner || input.owner),
293
+ side_effects: asList(input.side_effects || input.side_effect),
294
+ avoidance_note: avoidanceNote,
269
295
  study_note: studyNote
270
296
  };
271
297
  }
@@ -400,6 +426,12 @@ function normalizeOwnerRoute(value = '') {
400
426
  return `$${text}`;
401
427
  return text ? shortText(text, 80) : '';
402
428
  }
429
+ function normalizeLoopEvidenceLevel(value = '') {
430
+ const text = String(value || '').toLowerCase().replace(/[-\s]/g, '_');
431
+ if (['fixture', 'smoke', 'local', 'real'].includes(text))
432
+ return text;
433
+ return 'none';
434
+ }
403
435
  function shortText(value = '', limit = 240) {
404
436
  const text = String(value || '').trim().replace(/\s+/g, ' ');
405
437
  if (text.length <= limit)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yam-flow",
3
- "version": "0.1.9",
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,8 +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.
81
+ - Evidence level or stamp when the handoff relies on a specific command, file, or report.
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`.
84
+ - Owner route, scope owner, and side effects when more than one person or route may continue the work.
80
85
 
81
- `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`, and `owner_route`.
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`.
82
87
 
83
88
  ## Route Weight
84
89
 
@@ -74,4 +74,4 @@ Include:
74
74
  - Evidence gathered.
75
75
  - Truth status.
76
76
  - Blockers or remaining risk.
77
- - Handoff: fix-first items, remaining tasks, recommended direction, and why that next step matters.
77
+ - Handoff: fix-first items, remaining tasks, recommended direction, why that next step matters, blocker kind, safe retry, evidence level/stamp, side effects, and owner scope when another route or person may continue.
@@ -106,4 +106,4 @@ Report:
106
106
  - Evidence gathered.
107
107
  - Truth status.
108
108
  - Cleanup status.
109
- - Handoff: fix-first items, remaining tasks, recommended direction, and why that next step matters.
109
+ - Handoff: fix-first items, remaining tasks, recommended direction, why that next step matters, blocker kind, safe retry, evidence level/stamp, side effects, and owner scope when another route or person may continue.
@@ -79,4 +79,4 @@ Keep it compact:
79
79
  - Study Note when useful: what code was wrong, what role it has, how the symptom showed up, what changed, and why it matters in short non-specialist language.
80
80
  - What was checked.
81
81
  - What was skipped, blocked, or still risky.
82
- - Handoff when useful: fix-first items, remaining tasks, recommended direction, and why that next step matters.
82
+ - Handoff when useful: fix-first items, remaining tasks, recommended direction, why that next step matters, blocker kind, safe retry, evidence level/stamp, and owner scope when another route or person may continue.
@@ -172,4 +172,4 @@ Report:
172
172
  - States/viewports checked.
173
173
  - Study Note when UI implementation changed code: what UI code changed, what role it has, how the visible symptom appeared, and why the change matters in short non-specialist language.
174
174
  - Truth status and visual verification cap.
175
- - Handoff when useful: fix-first visual issues, remaining tasks, recommended direction, and why that next step matters.
175
+ - Handoff when useful: fix-first visual issues, remaining tasks, recommended direction, why that next step matters, blocker kind, safe retry, evidence level/stamp, and owner scope when another route or person may continue.
package/src/bin/yam.ts CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  detectDbSafetyText as detectTrustDbSafetyText,
25
25
  isTruthStatus
26
26
  } from '../lib/trust-kernel.js';
27
- import type { ToolIntent } from '../lib/trust-kernel.js';
27
+ import type { LoopEvidenceLevel, ToolIntent } from '../lib/trust-kernel.js';
28
28
 
29
29
  type AnyRecord = Record<string, any>;
30
30
 
@@ -525,6 +525,12 @@ function normalizeToolIntent(value = ''): ToolIntent {
525
525
  return 'read_only';
526
526
  }
527
527
 
528
+ function normalizeLoopEvidenceLevelFlag(value = ''): LoopEvidenceLevel {
529
+ const text = String(value || '').toLowerCase().replace(/[-\s]/g, '_');
530
+ if (['fixture', 'smoke', 'local', 'real'].includes(text)) return text as LoopEvidenceLevel;
531
+ return 'none';
532
+ }
533
+
528
534
  function uniqueNextActionDetails(actions = []) {
529
535
  const seen = new Set<string>();
530
536
  const unique = [];
@@ -1569,6 +1575,7 @@ function runReleaseReport() {
1569
1575
  const publishReadiness = releasePublishReadiness(checks, provenance, tarball, publishBlockerEvidence);
1570
1576
  const nextActions = releaseNextActions(checks, provenance, tarball, publishBlockerEvidence, publishReadiness);
1571
1577
  const ok = failed.length === 0 && publishReadiness.status === 'ready';
1578
+ const readinessReceipt = releaseReadinessReceipt(provenance, freshness, tarball, publishReadiness, ok);
1572
1579
  return {
1573
1580
  schema: 'yam.release-report.v1',
1574
1581
  generated_at: startedAt,
@@ -1582,12 +1589,55 @@ function runReleaseReport() {
1582
1589
  tarball,
1583
1590
  freshness,
1584
1591
  publish_readiness: publishReadiness,
1592
+ readiness_receipt: readinessReceipt,
1585
1593
  study_note: releaseStudyNote(publishReadiness, publishBlockerEvidence),
1586
1594
  publishBlockerEvidence,
1587
1595
  next_actions: nextActions
1588
1596
  };
1589
1597
  }
1590
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
+
1591
1641
  function releaseProvenance(checks = []) {
1592
1642
  const gitCommit = runReadOnlyCommand('git', ['rev-parse', '--short', 'HEAD']);
1593
1643
  const gitStatus = runReadOnlyCommand('git', ['status', '--short']);
@@ -1971,24 +2021,34 @@ function loopUsage() {
1971
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.
1972
2022
 
1973
2023
  Usage:
1974
- yam loop report [--route route] [--intent text] [--loop-kind harness|release|ueye|scout|deep|mission] [--stage id:status:note] [--evidence text] [--blocked text] [--fix-first-item text] [--remaining-task text] [--recommended-direction text] [--implementation-note text] [--why-this-next text] [--blocked-by text] [--owner-route route] [--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]
1975
2025
  `);
1976
2026
  }
1977
2027
 
1978
2028
  async function loopReport(args = []) {
1979
2029
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return loopUsage();
1980
- const flags = parseSimpleFlags(args, new Set(['route', 'intent', 'loop-kind', 'stage', 'evidence', 'blocked', 'blocker', 'next-action', 'fix-first-item', 'remaining-task', 'recommended-direction', 'direction', 'implementation-note', 'why-this-next', 'blocked-by', 'owner-route', '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']));
1981
2031
  const blockers = [...arrayFlag(flags.blocked), ...arrayFlag(flags.blocker)];
1982
2032
  const report = buildLoopReport({
1983
2033
  route: normalizeRoute(flags.route) || String(flags.route || ''),
1984
2034
  intent: String(flags.intent || ''),
1985
2035
  loop_kind: String(flags.loop_kind || flags.route || 'harness'),
2036
+ stage_conventions: arrayFlag(flags.stage_convention),
1986
2037
  stages: arrayFlag(flags.stage),
1987
2038
  evidence: arrayFlag(flags.evidence),
2039
+ evidence_level: normalizeLoopEvidenceLevelFlag(flags.evidence_level),
2040
+ evidence_stamp: String(flags.evidence_stamp || ''),
2041
+ source_digest: String(flags.source_digest || ''),
2042
+ covered_requirements: arrayFlag(flags.covered_requirement),
2043
+ uncovered_requirements: arrayFlag(flags.uncovered_requirement),
1988
2044
  blockers,
2045
+ blocked_kind: String(flags.blocked_kind || ''),
2046
+ failure_cause: String(flags.failure_cause || ''),
1989
2047
  truth_status: isTruthStatus(flags.truth) ? flags.truth : undefined,
1990
2048
  intent_label: normalizeToolIntent(flags.intent_label || flags.tool_intent || 'read_only'),
1991
2049
  next_action: String(flags.next_action || ''),
2050
+ safe_retry: String(flags.safe_retry || ''),
2051
+ recovery_hint: String(flags.recovery_hint || ''),
1992
2052
  fix_first_items: arrayFlag(flags.fix_first_item),
1993
2053
  remaining_tasks: arrayFlag(flags.remaining_task),
1994
2054
  recommended_direction: String(flags.recommended_direction || flags.direction || ''),
@@ -1996,6 +2056,10 @@ async function loopReport(args = []) {
1996
2056
  why_this_next: String(flags.why_this_next || ''),
1997
2057
  blocked_by: arrayFlag(flags.blocked_by),
1998
2058
  owner_route: String(flags.owner_route || flags.route || ''),
2059
+ owner_scope: [...arrayFlag(flags.owner_scope), ...arrayFlag(flags.scope)],
2060
+ scope_owner: String(flags.scope_owner || flags.owner || ''),
2061
+ side_effects: arrayFlag(flags.side_effect),
2062
+ avoidance_note: String(flags.avoidance_note || ''),
1999
2063
  issue_code: String(flags.issue_code || ''),
2000
2064
  issue_role: String(flags.issue_role || ''),
2001
2065
  issue_symptom: String(flags.issue_symptom || ''),
@@ -18,6 +18,7 @@ export type RuntimeSubsystem = 'real_runtime' | 'tmux_physical' | 'process_clean
18
18
  export type RuntimeBackend = 'none' | 'in_app_browser' | 'playwright' | 'terminal' | 'tmux' | 'zellij' | 'unknown';
19
19
  export type RuntimeClaim = 'not_started' | 'started' | 'observed' | 'stopped' | 'cleanup_verified' | 'blocked' | 'unknown';
20
20
  export type LoopStageStatus = 'passed' | 'failed' | 'blocked' | 'skipped' | 'partial' | 'pending' | 'recorded';
21
+ export type LoopEvidenceLevel = 'none' | 'fixture' | 'smoke' | 'local' | 'real';
21
22
  export type ToolIntent = 'read_only' | 'write' | 'destructive' | 'runtime' | 'visual' | 'publish';
22
23
 
23
24
  export interface SafetyHit {
@@ -182,13 +183,23 @@ export interface LoopReport {
182
183
  route: string;
183
184
  intent: string;
184
185
  loop_kind: string;
186
+ stage_conventions: string[];
185
187
  stages: LoopStage[];
186
188
  evidence: string[];
189
+ evidence_level: LoopEvidenceLevel;
190
+ evidence_stamp: string;
191
+ source_digest: string;
192
+ covered_requirements: string[];
193
+ uncovered_requirements: string[];
187
194
  blockers: string[];
195
+ blocked_kind: string;
196
+ failure_cause: string;
188
197
  truth_status: TruthStatus;
189
198
  intent_label: ToolIntent;
190
199
  tool_intent: ToolIntent;
191
200
  next_action: string;
201
+ safe_retry: string;
202
+ recovery_hint: string;
192
203
  fix_first_items: string[];
193
204
  remaining_tasks: string[];
194
205
  recommended_direction: string;
@@ -196,6 +207,10 @@ export interface LoopReport {
196
207
  why_this_next: string;
197
208
  blocked_by: string[];
198
209
  owner_route: string;
210
+ owner_scope: string[];
211
+ scope_owner: string;
212
+ side_effects: string[];
213
+ avoidance_note: string;
199
214
  study_note: StudyNote;
200
215
  }
201
216
 
@@ -540,10 +555,20 @@ export function buildLoopReport(input: Partial<LoopReport> & Record<string, unkn
540
555
  ? input.stages.map((stage) => buildLoopStage(stage))
541
556
  : [];
542
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);
543
560
  const blockers = asList(input.blockers || input.blocked);
561
+ const blockedBy = asList(input.blocked_by);
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);
566
+ const evidenceStamp = shortText(input.evidence_stamp || input.source_digest, 320);
567
+ const sourceDigest = shortText(input.source_digest || input.evidence_stamp, 320);
544
568
  const hasBlockedStage = stages.some((stage) => stage.status === 'blocked' || stage.status === 'failed');
569
+ const hasBlockerSignal = Boolean(blockers.length || blockedBy.length || blockedKind || uncoveredRequirements.length);
545
570
  const requestedTruth = isTruthStatus(input.truth_status) ? input.truth_status : '';
546
- const derivedTruth: TruthStatus = blockers.length || hasBlockedStage ? 'blocked'
571
+ const derivedTruth: TruthStatus = hasBlockerSignal || hasBlockedStage ? 'blocked'
547
572
  : evidence.length && stages.some((stage) => stage.status === 'passed') ? 'verified'
548
573
  : evidence.length || stages.length ? 'partial'
549
574
  : 'assumed';
@@ -556,20 +581,36 @@ export function buildLoopReport(input: Partial<LoopReport> & Record<string, unkn
556
581
  route: String(input.route || ''),
557
582
  intent: shortText(input.intent),
558
583
  loop_kind: String(input.loop_kind || 'harness'),
584
+ stage_conventions: asList(input.stage_conventions || input.stage_convention).length
585
+ ? asList(input.stage_conventions || input.stage_convention)
586
+ : ['brief', 'plan', 'implement', 'critique', 'verify', 'handoff'],
559
587
  stages,
560
588
  evidence,
589
+ evidence_level: normalizeLoopEvidenceLevel(input.evidence_level),
590
+ evidence_stamp: evidenceStamp,
591
+ source_digest: sourceDigest,
592
+ covered_requirements: coveredRequirements,
593
+ uncovered_requirements: uncoveredRequirements,
561
594
  blockers,
595
+ blocked_kind: blockedKind,
596
+ failure_cause: failureCause,
562
597
  truth_status: truth,
563
598
  intent_label: intentLabel,
564
599
  tool_intent: intentLabel,
565
- next_action: shortText(input.next_action || (blockers[0] ? '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')),
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,
566
603
  fix_first_items: asList(input.fix_first_items || input.fix_first_item),
567
604
  remaining_tasks: asList(input.remaining_tasks || input.remaining_task),
568
605
  recommended_direction: shortText(input.recommended_direction || input.direction),
569
606
  implementation_notes: asList(input.implementation_notes || input.implementation_note),
570
607
  why_this_next: shortText(input.why_this_next),
571
- blocked_by: asList(input.blocked_by),
608
+ blocked_by: blockedBy,
572
609
  owner_route: normalizeOwnerRoute(input.owner_route || input.route),
610
+ owner_scope: asList(input.owner_scope || input.scope),
611
+ scope_owner: normalizeOwnerRoute(input.scope_owner || input.owner),
612
+ side_effects: asList(input.side_effects || input.side_effect),
613
+ avoidance_note: avoidanceNote,
573
614
  study_note: studyNote
574
615
  };
575
616
  }
@@ -694,6 +735,12 @@ function normalizeOwnerRoute(value: unknown = ''): string {
694
735
  return text ? shortText(text, 80) : '';
695
736
  }
696
737
 
738
+ function normalizeLoopEvidenceLevel(value: unknown = ''): LoopEvidenceLevel {
739
+ const text = String(value || '').toLowerCase().replace(/[-\s]/g, '_');
740
+ if (['fixture', 'smoke', 'local', 'real'].includes(text)) return text as LoopEvidenceLevel;
741
+ return 'none';
742
+ }
743
+
697
744
  function shortText(value: unknown = '', limit = 240): string {
698
745
  const text = String(value || '').trim().replace(/\s+/g, ' ');
699
746
  if (text.length <= limit) return text;
package/yam.manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yam",
3
- "version": "0.1.9",
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, and recommended next direction 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": [