@ngockhoale/ukit 3.4.72 → 3.4.73

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,11 @@
2
2
 
3
3
  All notable changes to UKit are documented here.
4
4
 
5
+ ## 3.4.73 - 2026-10-28
6
+
7
+ - Git history proof tolerates unrelated untracked files; tracked/staged drift and explicit whole-tree clean obligations remain strict. No filename exemptions.
8
+ - Native ledger-to-Stop fixtures cover Claude Code/Codex/omp, repeated Stop and untracked shipped-source publication drift; not live-host certification.
9
+
5
10
  ## 3.4.72 - 2026-10-28
6
11
 
7
12
  - Native evidence: reject background, masked and test-expression exits as operation/verification proof; retain supported foreground controls across shared Claude Code/Codex/OMP fixtures. No live-host certification.
@@ -4,6 +4,7 @@ description: >-
4
4
  Machine-readable ownership/source registry for every documentation surface.
5
5
  Hand-authored per SPEC §7; enforced by tests/consistency/docRegistry.test.js.
6
6
  entries:
7
+ - {id: post-3472-git, path: docs/analysis/POST-3472-GIT-OPERATION-VERIFICATION.md, class: canonical, audience: [maintainer], owner: release, source_of_truth: docs/analysis/POST-3472-GIT-OPERATION-VERIFICATION.md, merge_strategy: none, load_policy: on-demand, validation: [manual], archive_policy: never}
7
8
  - {id: release-3472, path: docs/analysis/RELEASE-3472-EVIDENCE-INTEGRITY.md, class: canonical, audience: [maintainer, agent], owner: release, source_of_truth: docs/analysis/RELEASE-3472-EVIDENCE-INTEGRITY.md, merge_strategy: none, load_policy: on-demand, validation: [manual], archive_policy: never}
8
9
  - id: systematic-loop-audit-2026-10-26
9
10
  path: docs/analysis/SYSTEMATIC-LOOP-AUDIT-2026-10-26.md
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngockhoale/ukit",
3
- "version": "3.4.72",
3
+ "version": "3.4.73",
4
4
  "description": "Install/update an index-first AI workspace for Claude Code, OpenAI Codex and omp (Oh My Pi).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -50,17 +50,12 @@ const SERVICE_MUTATION = /^(?:docker(?:\s+compose)?|docker-compose)\s+(?:restart
50
50
  // Remote authority, not stale tracking refs: gate binds sha to clean live HEAD/tracking
51
51
  // remote. Native mutation success remains a separate requirement.
52
52
  const GIT_LS_REMOTE = /^git\s+ls-remote\s+(\S+)\s+(refs\/heads\/\S+)\s*$/;
53
- // Native GitHub repository postcondition: `gh repo view [repo] --json url,visibility` asks GitHub itself
54
- // (like ls-remote asks the Git remote). Strict: unmasked single command, BOTH JSON fields present, enum
55
- // visibility — the gate then binds the observed url to the gate-time Git remote URL
56
- // (ledger.gitAuthority.remoteUrl), so a masked/public/wrong-repo reply never completes publication.
53
+ // Native GitHub visibility needs both fields; the gate binds the observed URL to the live remote.
57
54
  const GH_REPO_VIEW = /^gh\s+repo\s+view(?:\s+(\S+))?\s+--json\s+([A-Za-z0-9_,]+)\s*$/;
58
55
  const GH_VISIBILITY = /^(?:PUBLIC|PRIVATE|INTERNAL)$/;
59
56
  const DRY_RUN = /(?:^|\s)(?:--dry-run|--check|--no-act|-n)(?:\s|=|$)/;
60
57
  const CURL = /^curl\s/;
61
- // A leading `cd <dir>;` / `cd <dir> &&` only selects the working directory: it prints nothing and the shell's
62
- // exit status is the next command's, so it must not turn an otherwise single, checked command into a "masked"
63
- // compound. Only ONE leading cd is dropped; any other `;`, `||`, pipe or extra segment still masks.
58
+ // Drop one leading cd; remaining separators still mask native exit attribution.
64
59
  const LEADING_CD = /^\s*cd\s+(?:"[^"\n$`\\]*"|'[^'\n]*'|[^\s;&|<>$`()]+)\s*(?:;|&&)\s*(?=\S)/;
65
60
  export function stripLeadingCd(command) {
66
61
  return typeof command === 'string' ? command.replace(LEADING_CD, '') : command;
@@ -154,7 +149,10 @@ export function bashOperationReceipt(rawCommand, response, transportSuccess, goa
154
149
  const wholeTree = kind !== 'status' || !/\s--\s|\s--porcelain=\S*\s+\S|(?:--porcelain|-s|--short)\s+[^\s-]/.test(seg);
155
150
  if (!wholeTree || (wanted && kind !== wanted)) continue;
156
151
  role = 'state';
157
- proven = kind === 'status' ? hasOutputField && text === '' : true;
152
+ const historyOnly = required.length > 0 && required.every((verb) => ['commit', 'push', 'tag'].includes(verb));
153
+ const untrackedOnly = /^git\s+status\s+--porcelain\s*$/.test(seg)
154
+ && text.split('\n').every((line) => !line || line.startsWith('?? '));
155
+ proven = kind === 'status' ? hasOutputField && (text === '' || (historyOnly && untrackedOnly)) : true;
158
156
  } else if (GIT_VERB.test(seg) && role !== 'state') {
159
157
  role = 'mutation';
160
158
  target = seg.match(GIT_VERB)[1];
@@ -177,15 +175,10 @@ export function bashOperationReceipt(rawCommand, response, transportSuccess, goa
177
175
  }
178
176
  }
179
177
  if (!role) return null;
180
- // A git STATE observation is attributable only when it is one unmasked, transport-successful command: in a
181
- // compound/masked chain the stdout of its parts is mixed, so it can neither prove nor refute the state and
182
- // must not overwrite earlier authoritative evidence. (A standalone dirty status still records as failed.)
178
+ // Mixed/masked stdout cannot prove or refute state; standalone dirty status still fails.
183
179
  if (domain === 'git-integrity' && role === 'state'
184
180
  && (masked || real.filter((seg) => seg && !/^cd\s+\S+$/.test(seg)).length !== 1)) return null;
185
- // A failed `gh repo view` (nonzero exit / unparseable JSON) IS an observation: it records as a FAILED
186
- // state receipt (no github field) so it invalidates any earlier PRIVATE proof until a fresh successful
187
- // view lands — the postcondition must hold at the LATEST check, not at some earlier one. Other git
188
- // state commands keep the rule that a transport failure is unattributable and records nothing.
181
+ // Failed GitHub views invalidate prior visibility; other failed Git transports mint no state proof.
189
182
  if (domain === 'git-integrity' && role === 'state' && transportSuccess !== true && !ghObserved) return null;
190
183
  const ok = transportSuccess === true && !masked && Boolean(scope) && (role === 'mutation' || proven);
191
184
  return { kind: 'operation', success: ok, transportSuccess: transportSuccess === true, harness,
@@ -3780,15 +3780,17 @@ function gitAuthority(cwd) {
3780
3780
  const remote = gitOutput(['config', `branch.${branch}.remote`], cwd);
3781
3781
  const remoteUrl = remote ? gitOutput(['config', `remote.${remote}.url`], cwd) : null;
3782
3782
  // gitOutput conflates empty output with failure; cleanliness needs an explicit successful status read.
3783
- let clean = false;
3783
+ let clean = false; let trackedClean = false;
3784
3784
  try {
3785
- clean = execFileSync('git', ['status', '--porcelain'], {
3785
+ const status = execFileSync('git', ['status', '--porcelain'], {
3786
3786
  cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000,
3787
- }).trim() === '';
3787
+ });
3788
+ clean = status.trim() === '';
3789
+ trackedClean = status.split('\n').every((line) => !line || line.startsWith('?? '));
3788
3790
  } catch { /* an unreadable working tree is not clean */ }
3789
3791
  // Annotated tags are peeled by --points-at; this state corroborates successful native tag execution.
3790
3792
  const tagsAtHead = (gitOutput(['tag', '--points-at', 'HEAD'], cwd) || '').split('\n').map((t) => t.trim()).filter(Boolean).slice(0, 16);
3791
- return { head, branch, remote, remoteUrl, tagsAtHead, clean };
3793
+ return { head, branch, remote, remoteUrl, tagsAtHead, clean, trackedClean };
3792
3794
  }
3793
3795
  function withGitAuthority(ledger, cwd) {
3794
3796
  const gitAuth = ledger && Object.keys(ledger).length ? gitAuthority(cwd) : null;
@@ -49,10 +49,7 @@ export function foldOperationEvidence(bank = {}, receipt, ledger) {
49
49
  if (receipt.toolUseId && (seen.includes(receipt.toolUseId) || Object.values(bank).some((entry) =>
50
50
  [entry?.mutation, entry?.state].some((proof) => proof?.toolUseId === receipt.toolUseId)))) return bank;
51
51
  if (receipt.toolUseId && (!bounded(receipt.toolUseId) || seen.length >= 256)) return { ...bank, overflow: true };
52
- // A remote-parity observation (git ls-remote) must survive the clean-tree status check that normally follows
53
- // it: both are `state` receipts under one key, so the later status used to overwrite the remote proof. The
54
- // proof is stamped with the sequence it was observed at and carried over only while no mutation (failed or
55
- // not) of this domain/scope was recorded after it; the gate still compares its sha to live HEAD.
52
+ // Carry remote parity across successful state checks only until the next mutation; gate checks live HEAD.
56
53
  const lastMutation = Object.values(bank).reduce((max, entry) => {
57
54
  const m = entry?.mutation;
58
55
  return m?.operation?.domain === op.domain && m.operation.scope === op.scope && Number.isSafeInteger(m.sequence) ? Math.max(max, m.sequence) : max;
@@ -127,15 +124,15 @@ export function operationEvidenceStatus(goal, ledger = {}) {
127
124
  if (goal.visibility === 'PRIVATE' && !(validGithubView(op?.github)
128
125
  && op.github.visibility === 'PRIVATE' && githubRepository(auth?.remoteUrl) !== null
129
126
  && githubRepository(op.github.url) === githubRepository(auth.remoteUrl))) return incomplete;
130
- if (goal.domain === 'git-integrity' && executed && validRemoteSync(op.remoteSync) && auth && auth.clean === true
127
+ // Commit/push prove committed history; an explicit clean goal still owns the whole tree.
128
+ const cleanEnough = auth?.clean === true || (auth?.trackedClean === true && !requiredTargets.includes('clean'));
129
+ if (goal.domain === 'git-integrity' && executed && validRemoteSync(op.remoteSync) && cleanEnough
131
130
  && requiredTargets.every((t) => t === 'commit' || t === 'push' || t === 'clean' || (t === 'tag' && Array.isArray(auth.tagsAtHead) && auth.tagsAtHead.length > 0))
132
131
  && attempts.every((r) => (Number.isSafeInteger(op.remoteSync.observedSequence) ? op.remoteSync.observedSequence : observation.sequence) > r.sequence)
133
132
  && op.remoteSync.sha === auth.head && op.remoteSync.ref === `refs/heads/${auth.branch}`
134
133
  && op.remoteSync.remote === auth.remote) return { complete: true, operationProof: 'remote-parity' };
135
- // Push promises remote publication. No local check or missing gate-time authority can discharge it.
134
+ // Push needs remote proof; divergent observations cannot fall back to local checks.
136
135
  if (goal.domain === 'git-integrity' && requiredTargets.includes('push')) return incomplete;
137
- // A remote observation that disagrees with live local truth is divergence, not a verified state: it can never
138
- // be discharged by the fallback below just because a push command exited 0.
139
136
  if (goal.domain === 'git-integrity' && validRemoteSync(op.remoteSync) && auth
140
137
  && (op.remoteSync.sha !== auth.head || op.remoteSync.ref !== `refs/heads/${auth.branch}`
141
138
  || (auth.remote && op.remoteSync.remote !== auth.remote))) return incomplete;