gsdd-cli 0.19.3 → 0.21.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/README.md CHANGED
@@ -346,6 +346,7 @@ Workflows are agent skills or commands, not plain shell utilities. How you invok
346
346
  | `npx -y gsdd-cli init [--tools <platform>]` | Set up `.planning/`, generate skills/adapters |
347
347
  | `npx -y gsdd-cli update [--tools <platform>] [--templates]` | Regenerate skills/adapters and refresh the repo-local helper runtime; `--templates` refreshes `.planning/templates/` and role contracts |
348
348
  | `npx -y gsdd-cli health [--json]` | Check workspace integrity and generated-surface freshness (healthy/degraded/broken) |
349
+ | `npx -y gsdd-cli ui-proof validate <path> [--claim <public\|publication\|tracked\|delivery\|release>]` | Validate UI proof bundle metadata without requiring browser tooling; use `--claim` only when validating that stronger proof use |
349
350
  | `npx -y gsdd-cli file-op <copy\|delete\|regex-sub>` | Run deterministic workspace-confined file copy, delete, and regex substitution |
350
351
  | `npx -y gsdd-cli find-phase [N]` | Show phase info as JSON (for agent consumption) |
351
352
  | `npx -y gsdd-cli phase-status <N> <status>` | Update a single ROADMAP phase status through the status-aware helper |
@@ -209,6 +209,9 @@ Before reporting a task complete:
209
209
  - if a UI change is involved, verify the relevant rendering path
210
210
  - if an API change is involved, hit the endpoint or targeted integration path
211
211
  - A task is not complete because code was written. It is complete when the intended verification path actually passes.
212
+
213
+ ### UI Proof Execution
214
+ If the plan defines UI proof slots, record observed proof against the exact claim, route/state, observation, evidence kind, artifact path or manual step, privacy metadata, result, and claim limit before claiming task completion. Artifact metadata must include `visibility`, `retention`, `sensitivity`, and `safe_to_publish`; raw screenshots, traces, videos, DOM snapshots, and reports are local-only/unsafe by default and cannot back public, tracked, delivery, release, or publication proof claims. Use `gsdd ui-proof validate <path>` or `gsdd health` when a bundle exists. Artifact count, source comments, AST/cAST findings, semantic search, and Semble-like retrieval are not proof. Missing or weakly linked evidence must be recorded as proof debt, waiver, deferment, or reduced claim language rather than satisfied proof.
212
215
  </execution_loop>
213
216
 
214
217
  <checkpoint_protocol>
package/agents/planner.md CHANGED
@@ -153,6 +153,14 @@ Any checkpoint must be justified by the task itself, not by planner caution or h
153
153
  Any plan containing `checkpoint:*` must set `autonomous: false`.
154
154
  </task_contract>
155
155
 
156
+ <ui_proof_planning>
157
+ For UI-sensitive work, plan proof slots that can later be matched exactly to claim, route/state, observation, evidence kind, artifact path or manual step, privacy metadata, result, and claim limit. Use only the stable evidence kinds `code`, `test`, `runtime`, `delivery`, and `human`.
158
+
159
+ Require observed artifacts to carry `visibility`, `retention`, `sensitivity`, and `safe_to_publish`; when a planned slot is meant to support public, publication, tracked, delivery, or release proof, say to validate the observed bundle with `gsdd ui-proof validate <path> --claim <...>`. `gsdd ui-proof validate`/`gsdd health` must catch invalid bundle metadata when present.
160
+
161
+ Do not let source annotations, AST/cAST findings, semantic search, comments, or Semble-like retrieval satisfy proof slots; they are discovery hints only. Human acceptance can narrow or waive a claim and record proof debt, but it must not turn missing or mismatched non-human evidence into `satisfied` proof.
162
+ </ui_proof_planning>
163
+
156
164
  <dependency_graph_example>
157
165
  Example dependency graph:
158
166
 
@@ -184,7 +192,7 @@ Wave rule:
184
192
  Write one or more `PLAN.md` files to the phase directory.
185
193
 
186
194
  Keep the current GSDD schema exactly:
187
- - frontmatter keys: `phase`, `plan`, `type`, `wave`, `runtime`, `assurance`, `depends_on`, `files-modified`, `autonomous`, `requirements`, `non_goals`, `hard_boundaries`, `escalation_triggers`, `approval_gates`, `anti_regression_targets`, `known_unknowns`, `high_leverage_surfaces`, `second_pass_required`, `closure_claim_limit`, `parallelism_budget`, `leverage`, `must_haves`
195
+ - frontmatter keys: `phase`, `plan`, `type`, `wave`, `runtime`, `assurance`, `depends_on`, `files-modified`, `autonomous`, `requirements`, `non_goals`, `hard_boundaries`, `escalation_triggers`, `approval_gates`, `anti_regression_targets`, `known_unknowns`, `ui_proof_slots`, `no_ui_proof_rationale`, `high_leverage_surfaces`, `second_pass_required`, `closure_claim_limit`, `parallelism_budget`, `leverage`, `must_haves`
188
196
  - typed tasks with `files`, `action`, `verify`, and `done`
189
197
 
190
198
  Typed frontmatter example:
@@ -215,6 +223,8 @@ anti_regression_targets:
215
223
  - Existing session middleware behavior remains unchanged for already-supported routes.
216
224
  known_unknowns:
217
225
  - Exact copy wording for auth errors may still need product confirmation.
226
+ ui_proof_slots: []
227
+ no_ui_proof_rationale: Not UI-sensitive; scoped work does not claim a visible UI outcome.
218
228
  high_leverage_surfaces: []
219
229
  second_pass_required: false
220
230
  closure_claim_limit: Do not claim phase completion until verification satisfies the evidence contract for the scoped truths.
@@ -123,6 +123,8 @@ Do not return a flat symptom list when the same underlying breakage explains mul
123
123
 
124
124
  Visual correctness, live interaction quality, and some external integrations still need explicit human checks.
125
125
 
126
+ For UI proof slots, fail closed unless observed proof is matched to the exact claim, route/state, observation, evidence kind, artifact path or manual step, privacy metadata, result, and claim limit. Artifact metadata must include `visibility`, `retention`, `sensitivity`, and `safe_to_publish`; local-only or unsafe artifacts cannot back public, tracked, delivery, release, or publication proof claims, and `gsdd ui-proof validate`/`gsdd health` metadata failures block the stronger proof claim. Screenshots, traces, reports, Gherkin, a11y scans, E2E outputs, manual notes, source annotations, AST/cAST findings, semantic search, comments, and Semble-like retrieval do not satisfy proof by existence alone. Human acceptance records risk, waiver, deferment, proof debt, or a narrowed claim; it does not upgrade missing or mismatched non-human proof to `satisfied`.
127
+
126
128
  ## Step 9: Determine overall status
127
129
 
128
130
  - `passed` when all programmatic checks pass and no human-only checks remain
package/bin/gsdd.mjs CHANGED
@@ -19,6 +19,7 @@ import { cmdFileOp } from './lib/file-ops.mjs';
19
19
  import { createCmdHealth } from './lib/health.mjs';
20
20
  import { cmdLifecyclePreflight } from './lib/lifecycle-preflight.mjs';
21
21
  import { cmdSessionFingerprint } from './lib/session-fingerprint.mjs';
22
+ import { cmdUiProof } from './lib/ui-proof.mjs';
22
23
  import { resolveWorkspaceContext } from './lib/workspace-root.mjs';
23
24
 
24
25
  const __filename = fileURLToPath(import.meta.url);
@@ -107,6 +108,7 @@ const COMMANDS = {
107
108
  'file-op': cmdFileOp,
108
109
  'lifecycle-preflight': cmdLifecyclePreflight,
109
110
  'session-fingerprint': cmdSessionFingerprint,
111
+ 'ui-proof': cmdUiProof,
110
112
  'find-phase': cmdFindPhase,
111
113
  'phase-status': cmdPhaseStatus,
112
114
  verify: cmdVerify,
@@ -132,8 +134,5 @@ async function runCli(cliCommand = command, ...cliArgs) {
132
134
  await COMMANDS[cliCommand](...normalizedArgs);
133
135
  }
134
136
 
135
- if (IS_MAIN) {
136
- await runCli();
137
- }
138
-
139
- export { cmdHelp, cmdInit, cmdUpdate, cmdModels, cmdHealth, cmdFileOp, cmdLifecyclePreflight, cmdSessionFingerprint, cmdFindPhase, cmdPhaseStatus, cmdVerify, cmdScaffold, runCli, FRAMEWORK_VERSION, createCliContext };
137
+ if (IS_MAIN) await runCli();
138
+ export { cmdHelp, cmdInit, cmdUpdate, cmdModels, cmdHealth, cmdFileOp, cmdLifecyclePreflight, cmdSessionFingerprint, cmdUiProof, cmdFindPhase, cmdPhaseStatus, cmdVerify, cmdScaffold, runCli, FRAMEWORK_VERSION, createCliContext };
@@ -4,12 +4,13 @@
4
4
  // evaluate once, so CWD must be computed inside function bodies.
5
5
 
6
6
  import { existsSync, readFileSync, readdirSync } from 'fs';
7
- import { join } from 'path';
7
+ import { join, relative } from 'path';
8
8
  import { readManifest, detectModifications } from './manifest.mjs';
9
9
  import { output } from './cli-utils.mjs';
10
10
  import { runTruthChecks, TRUTH_CHECK_IDS } from './health-truth.mjs';
11
11
  import { evaluateLifecycleState } from './lifecycle-state.mjs';
12
12
  import { evaluateRuntimeFreshness } from './runtime-freshness.mjs';
13
+ import { findUiProofBundleFiles, readUiProofBundleFile, validateUiProofBundle } from './ui-proof.mjs';
13
14
  import { resolveWorkspaceContext } from './workspace-root.mjs';
14
15
 
15
16
  /**
@@ -31,7 +32,7 @@ export function createCmdHealth(ctx) {
31
32
  }
32
33
  const cwd = workspaceRoot;
33
34
  const frameworkSourceMode = isFrameworkSourceRepo(cwd);
34
- const healthCheckIds = ['E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'E7', 'E8', 'E9', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', ...TRUTH_CHECK_IDS, 'I1', 'I2', 'I3'];
35
+ const healthCheckIds = ['E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'E7', 'E8', 'E9', 'E10', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', ...TRUTH_CHECK_IDS, 'I1', 'I2', 'I3'];
35
36
 
36
37
  // Pre-init guard
37
38
  if (!existsSync(join(planningDir, 'config.json'))) {
@@ -123,7 +124,7 @@ export function createCmdHealth(ctx) {
123
124
  }
124
125
 
125
126
  // E8: critical root template files missing
126
- const requiredRootFiles = ['spec.md', 'roadmap.md', 'auth-matrix.md'];
127
+ const requiredRootFiles = ['spec.md', 'roadmap.md', 'auth-matrix.md', 'ui-proof.md'];
127
128
  const missingRoot = requiredRootFiles.filter((f) => !existsSync(join(templatesDir, f)));
128
129
  if (missingRoot.length > 0) {
129
130
  errors.push({ id: 'E8', severity: 'ERROR', message: `.planning/templates/ missing critical root files: ${missingRoot.join(', ')}`, fix: 'Run `npx -y gsdd-cli update --templates`' });
@@ -140,6 +141,23 @@ export function createCmdHealth(ctx) {
140
141
  }
141
142
  }
142
143
 
144
+ // E10: known UI proof bundles must satisfy deterministic metadata/privacy validation.
145
+ for (const bundlePath of findUiProofBundleFiles(planningDir)) {
146
+ const relativePath = relative(cwd, bundlePath).replace(/\\/g, '/');
147
+ const parsed = readUiProofBundleFile(bundlePath);
148
+ const validation = parsed.errors.length > 0
149
+ ? { valid: false, errors: parsed.errors }
150
+ : validateUiProofBundle(parsed.bundle);
151
+ if (!validation.valid) {
152
+ errors.push({
153
+ id: 'E10',
154
+ severity: 'ERROR',
155
+ message: `${relativePath} has invalid UI proof metadata (${validation.errors.map((entry) => entry.code).join(', ')})`,
156
+ fix: 'Run `gsdd ui-proof validate <path>` and add required privacy metadata, claim limits, fixed evidence kinds, observation artifact references, and safe-to-publish handling.',
157
+ });
158
+ }
159
+ }
160
+
143
161
  // --- WARNING checks ---
144
162
 
145
163
  // W1: generation-manifest.json missing
@@ -186,7 +186,12 @@ Commands:
186
186
  phase-status <N> <status> Update ROADMAP.md phase status ([ ] / [-] / [x])
187
187
  lifecycle-preflight <surface> [phase]
188
188
  Inspect deterministic lifecycle gate results for a workflow surface
189
- session-fingerprint write Rebaseline planning-state drift after reviewing changed planning files
189
+ session-fingerprint write [--allow-changed <ROADMAP.md,SPEC.md,config.json>]
190
+ Rebaseline planning-state drift after reviewing changed planning files
191
+ ui-proof validate <path> [--claim <public|publication|tracked|delivery|release>]
192
+ Validate UI proof metadata; use --claim for stronger proof uses
193
+ ui-proof compare <planned-slots-json> [observed-bundle-json ...]
194
+ Compare planned UI proof slots against observed bundles
190
195
  help Show this summary
191
196
 
192
197
  Platforms (for --tools):
@@ -257,6 +262,7 @@ Advanced/internal helpers (kept available, but not the primary first-run user st
257
262
  lifecycle-preflight Inspect deterministic lifecycle gate results for a workflow surface
258
263
  session-fingerprint Rebaseline the local planning-state fingerprint after review
259
264
  phase-status Update ROADMAP.md phase status through the local helper surface
265
+ ui-proof Validate UI proof metadata and compare planned slots to observed bundles
260
266
  file-op Deterministic workspace-confined file copy/delete/text mutation
261
267
  `;
262
268
  }
@@ -52,6 +52,11 @@ const SURFACE_POLICIES = {
52
52
  ownedWrites: ['spec', 'roadmap', 'phase-directories'],
53
53
  explicitLifecycleMutation: 'none',
54
54
  },
55
+ 'plan-milestone-gaps': {
56
+ classification: 'owned_write',
57
+ ownedWrites: ['roadmap', 'phase-directories'],
58
+ explicitLifecycleMutation: 'none',
59
+ },
55
60
  resume: {
56
61
  classification: 'owned_write',
57
62
  ownedWrites: ['checkpoint-cleanup'],
@@ -6,7 +6,7 @@ const BROWNFIELD_CHANGE_DIR = 'brownfield-change';
6
6
  const PHASE_LINE_RE = /^\s*[-*]\s*\[([ x-])\]\s*\*\*Phase\s+(\d+(?:\.\d+)*[a-z]?):\s*(.+?)\*\*(?:\s+—\s+\[([^\]]+)])?/i;
7
7
  const PHASE_DETAIL_HEADING_RE = /^(#{3,})\s+Phase\s+(\d+(?:\.\d+)*[a-z]?)(?::|\b)/i;
8
8
  const PHASE_DETAIL_STATUS_RE = /^\s*\*\*Status\*\*:\s*\[([ x-])\]/i;
9
- const ACTIVE_MILESTONE_HEADING_RE = /^###\s+(v[^\s]+)\s+(.+)$/im;
9
+ const ACTIVE_MILESTONE_HEADING_RE = /^#{2,3}\s+(v[^\s]+)\s+(.+)$/im;
10
10
  const MILESTONE_LEDGER_HEADING_RE = /^##\s+(?:✅\s+)?(v[^\s]+)\s*(?:—|-)?\s*(.*)$/i;
11
11
  const DETAILS_OPEN_RE = /<details\b/i;
12
12
  const DETAILS_CLOSE_RE = /<\/details>/i;
@@ -13,6 +13,7 @@ const HELPER_LIB_FILES = Object.freeze([
13
13
  'lifecycle-state.mjs',
14
14
  'phase.mjs',
15
15
  'session-fingerprint.mjs',
16
+ 'ui-proof.mjs',
16
17
  'workspace-root.mjs',
17
18
  ]);
18
19
 
@@ -47,6 +48,7 @@ import { cmdFileOp } from './lib/file-ops.mjs';
47
48
  import { cmdLifecyclePreflight } from './lib/lifecycle-preflight.mjs';
48
49
  import { cmdPhaseStatus } from './lib/phase.mjs';
49
50
  import { cmdSessionFingerprint } from './lib/session-fingerprint.mjs';
51
+ import { cmdUiProof } from './lib/ui-proof.mjs';
50
52
  import { bootstrapHelperWorkspace, consumeWorkspaceRootArg, resolveWorkspaceContext } from './lib/workspace-root.mjs';
51
53
 
52
54
  const COMMANDS = {
@@ -54,6 +56,7 @@ const COMMANDS = {
54
56
  'lifecycle-preflight': cmdLifecyclePreflight,
55
57
  'phase-status': cmdPhaseStatus,
56
58
  'session-fingerprint': cmdSessionFingerprint,
59
+ 'ui-proof': cmdUiProof,
57
60
  };
58
61
 
59
62
  function printHelp() {
@@ -69,8 +72,12 @@ function printHelp() {
69
72
  ' lifecycle-preflight <surface> [phase]',
70
73
  ' Inspect lifecycle gate results for a workflow surface',
71
74
  ' Example: node .planning/bin/gsdd.mjs lifecycle-preflight verify 1 --expects-mutation phase-status',
72
- ' session-fingerprint write',
75
+ ' session-fingerprint write [--allow-changed <ROADMAP.md,SPEC.md,config.json>]',
73
76
  ' Rebaseline planning-state drift after reviewing changed planning files',
77
+ ' ui-proof validate <path> [--claim <public|publication|tracked|delivery|release>]',
78
+ ' Validate UI proof metadata; use --claim for stronger proof uses',
79
+ ' ui-proof compare <planned-slots-json> [observed-bundle-json ...]',
80
+ ' Compare planned UI proof slots against observed bundles',
74
81
  '',
75
82
  'Advanced option:',
76
83
  ' --workspace-root <path> Override workspace root discovery before or after the subcommand',
@@ -63,16 +63,56 @@ export function cmdSessionFingerprint(...args) {
63
63
  return;
64
64
  }
65
65
 
66
- const [action] = normalizedArgs;
66
+ const [action, ...flags] = normalizedArgs;
67
67
  if (action !== 'write') {
68
- console.error('Usage: node .planning/bin/gsdd.mjs session-fingerprint write');
68
+ console.error('Usage: node .planning/bin/gsdd.mjs session-fingerprint write [--allow-changed <ROADMAP.md,SPEC.md,config.json>]');
69
69
  process.exitCode = 1;
70
70
  return;
71
71
  }
72
72
 
73
+ const allowChanged = parseAllowChanged(flags);
74
+ if (allowChanged.invalid) {
75
+ console.error('Usage: node .planning/bin/gsdd.mjs session-fingerprint write [--allow-changed <ROADMAP.md,SPEC.md,config.json>]');
76
+ process.exitCode = 1;
77
+ return;
78
+ }
79
+
80
+ if (allowChanged.files.length > 0) {
81
+ const drift = checkDrift(planningDir);
82
+ const changedFiles = drift.files.filter((file) => file.status !== 'unchanged').map((file) => file.file);
83
+ const unexpected = changedFiles.filter((file) => !allowChanged.files.includes(file));
84
+ if (unexpected.length > 0) {
85
+ output({
86
+ operation: 'session-fingerprint write',
87
+ changedFiles,
88
+ allowedChanged: allowChanged.files,
89
+ written: false,
90
+ reason: 'unexpected_planning_drift',
91
+ unexpected,
92
+ });
93
+ process.exitCode = 1;
94
+ return;
95
+ }
96
+ }
97
+
73
98
  output({ operation: 'session-fingerprint write', fingerprint: writeFingerprint(planningDir) });
74
99
  }
75
100
 
101
+ function parseAllowChanged(flags) {
102
+ const files = [];
103
+ for (let index = 0; index < flags.length; index += 1) {
104
+ if (flags[index] !== '--allow-changed') return { invalid: true, files: [] };
105
+ const value = flags[index + 1];
106
+ if (!value || value.startsWith('--')) return { invalid: true, files: [] };
107
+ files.push(...value.split(',').map((entry) => entry.trim()).filter(Boolean));
108
+ index += 1;
109
+ }
110
+ for (const file of files) {
111
+ if (!FINGERPRINT_SOURCES.includes(file)) return { invalid: true, files: [] };
112
+ }
113
+ return { invalid: false, files: [...new Set(files)] };
114
+ }
115
+
76
116
  /**
77
117
  * Read the stored fingerprint from .planning/.state-fingerprint.json.
78
118
  * Returns null if the file does not exist or is unparseable.
@@ -23,7 +23,7 @@ export function installProjectTemplates({ planningDir, distilledDir, agentsDir }
23
23
  console.log(` - WARN: missing expected template subdir: ${subdir}/`);
24
24
  }
25
25
  }
26
- const expectedRootFiles = ['spec.md', 'roadmap.md', 'auth-matrix.md'];
26
+ const expectedRootFiles = ['spec.md', 'roadmap.md', 'auth-matrix.md', 'ui-proof.md'];
27
27
  for (const file of expectedRootFiles) {
28
28
  if (!existsSync(join(localTemplatesDir, file))) {
29
29
  console.log(` - WARN: missing expected root template file: ${file}`);