leerness 1.36.301 → 1.36.303

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
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.36.303 - 2026-10-15
4
+
5
+ - Fixed (tests): `scripts/ci-shard.js` passed its own `LEERNESS_CI_SHARD_TIMES` to the leaves it runs, so `scripts/ci-shard-probe.js`, which runs the runner itself, overwrote the outer run's times file in the middle of a local full run (found in a full run for this release; the outer runner rewrote the file at its end, so the stored times were right). Leaves no longer see the variable, the way `e2eShard` already hides its own. CI does not set it. A new probe case fails on the 1.36.302 runner (13/14) and passes now (14/14) (T-0364).
6
+ - Not shipped: T-0357, removing the temp folders a test made after it ends with exit code 0, was planned for this release in a simplified form. Its four review rounds found 16 more problems in the exit-time decision and in which folders it records (6, 6, 2 and 2; the first 14 fixed, each with a test that fails on the reviewed code). By the owner's decision before the fourth round, this release ships only the change above, and T-0357 continues separately.
7
+ - Not changed: `contract verify` still counts exports written inside comments and strings. This was investigated and deferred on purpose in 1.36.18 (a comment masker without a parser deleted real code in review, which blocks valid contracts); T-0361 is on hold until a real parser is an option.
8
+
9
+ ## 1.36.302 - 2026-10-14
10
+
11
+ - Fixed (tests): store-values-probe's two `skill publish` tests failed when a run crossed UTC midnight (T-0358; seen once in a Linux Docker node:20 run during the 1.36.299 release).
12
+ - `skill publish` first runs a pre-check (1.9.98), which refuses while drift needs attention. drift reads the tracker's last date as UTC midnight with a one-day threshold, so a tracker dated the previous UTC day makes drift attention as soon as the UTC date changes. The probe makes its template project once, so a run that crosses UTC midnight between making the template and publishing fails.
13
+ - The two tests about the generated text now pass `--no-security-check`, as do the e2e cases B(1.9.47) and B(1.9.48). A new test checks the pre-check with dates that do not depend on the time of day: a tracker dated two days back is refused and publishes with `--no-security-check`; one dated a day ahead passes the pre-check.
14
+ - With the health and drift clock set one minute past the next UTC midnight (a preload in their processes), HEAD's store-values-probe fails both publish tests (11/13) and the new one passes 14/14; HEAD's two e2e cases fail and the new ones pass. On the real clock both pass.
15
+ - Of 18 commands compared with a tracker dated today and one dated yesterday (gate, health, audit, drift check, handoff, doctor, session close and others), only `health --strict` and `skill publish` change their exit code. The drift behavior itself is unchanged.
16
+ - Changed: CI shard weights for npm leaves now come from the first sharded CI run (T-0360; run 38481240201, 1.36.301), the median of each leaf's `ci-shard: ok` time over the Windows fast and gate commands jobs; 1.36.301 had measured them on a PC under load.
17
+ - From that run's own times, the slowest Windows fast shard goes from 28.8 to 23.9 min (15.0 / 26.5 / 28.8 measured). For `test:commands`, with each Node version's times predicted from weights of the other three versions: 26.2 / 25.7 / 24.1 / 23.2 to 26.0 / 23.2 / 23.4 / 22.3 min (Node 18 / 20 / 22 / 24).
18
+ - The e2e case weights stay those of 1.36.301. Predicted from the same run, the old split reproduces every measured shard time within 0.1 min. No reweighting from it (median, mean, maximum or normalized share over the four Windows Node versions, Node 18 alone, or the mean of both Node 18 runs) lowered the slowest Windows e2e shard, Node 18's 60.5 min as predicted (60.6 measured); each raised it, to 61.6-74.0 min.
19
+ - The run used 1,201.8 job-minutes, so with 20 jobs at a time it needs at least 60.1 min. It took 85.6 min from the first job's start to the last job's end; that last job, a Node 18 e2e shard, started 31.1 min after the first one (another started after 22.1 min). Windows e2e is 713.1 of those job-minutes, Node 18 alone 235.5 (1.38-1.63 times each other Node version).
20
+ - Not changed: drift still reads a tracker dated the previous UTC day as stale from the first minute of the new UTC day, which blocks `skill publish` until `--no-security-check` or a task update (reported, a product decision). `contract verify` counts exports written inside comments and strings (found during this release, T-0361).
21
+ - Reviewed by codex gpt-6.1-sol: four rounds on a candidate that also held a cleanup of test temp folders (T-0357); every finding concerned that cleanup, which moves to a later release (owner decision). A confirmation round on exactly this release's tree found no code issue and two CHANGELOG claims without saved evidence, the CI timing figures and the command sweep (NO-GO). Recomputed from the saved logs, the Windows e2e figure changed from 709 to 713.1 job-minutes and other figures were made exact; a check of the corrected entry against the saved evidence: GO.
22
+
3
23
  ## 1.36.301 - 2026-10-14
4
24
 
5
25
  - Changed: public CI runs its long work in parallel shards (T-0359). 1.36.300's CI run (38332288454) took five hours: each Windows gate job (199-300 min) ran e2e.js after `test:commands`, and the Windows fast job took 46 min (79 min in 1.36.299's run 38193803239).
package/README.md CHANGED
@@ -195,7 +195,7 @@ MIT
195
195
  <!-- leerness:project-readme:start -->
196
196
  ## Leerness Project Harness
197
197
 
198
- 이 프로젝트는 Leerness v1.36.301 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
198
+ 이 프로젝트는 Leerness v1.36.303 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
199
199
 
200
200
  ### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
201
201
 
@@ -249,7 +249,7 @@ leerness memory restore decision <date|title>
249
249
 
250
250
  ### MCP server (외부 AI 통합)
251
251
 
252
- Leerness v1.36.301는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **102개 도구**를 노출:
252
+ Leerness v1.36.303는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **102개 도구**를 노출:
253
253
 
254
254
  ```jsonc
255
255
  // 카테고리별
@@ -270,7 +270,7 @@ Leerness v1.36.301는 stdio JSON-RPC MCP server를 내장합니다 — Claude Co
270
270
  `<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
271
271
  1) 다음 라운드 후보 선정 → 2) 코드 변경 → 3) 회귀 테스트 갱신 → 4) 전체 e2e 스위트 통과 → 5) npm publish + git tag → 6) main push → 7) session close → 8) 다음 라운드 예약.
272
272
 
273
- 현재 사용 중인 하네스 버전: **1.36.301**. 공개 릴리스 여부는 공개 저장소의 태그와 배포 기록에서 별도로 확인합니다.
273
+ 현재 사용 중인 하네스 버전: **1.36.303**. 공개 릴리스 여부는 공개 저장소의 태그와 배포 기록에서 별도로 확인합니다.
274
274
 
275
275
  ### 성능 가이드
276
276
 
@@ -308,7 +308,7 @@ leerness release pack --close --auto-main-push
308
308
  - `.leerness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
309
309
  - `.leerness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
310
310
 
311
- Last synced by Leerness v1.36.301: 2026-09-30
311
+ Last synced by Leerness v1.36.303: 2026-09-30
312
312
  <!-- leerness:project-readme:end -->
313
313
  GitHub 로그인 계정 확인용 서버 어댑터: [설정·연결·승인과의 구분](docs/github-login.md). 실제 앱 등록/브라우저 세션 연결과 대상별 승인은 후속입니다.
314
314
 
package/bin/leerness.js CHANGED
@@ -115,7 +115,7 @@ const {
115
115
  migrateLegacyWorkspace,
116
116
  } = require('../lib/workspace-dir');
117
117
 
118
- const VERSION = '1.36.301';
118
+ const VERSION = '1.36.303';
119
119
 
120
120
  // MCP lifecycle 주소 표식은 현재 CLI 호출 한 번에만 유효하다. CLI bootstrap에서 즉시 env에서
121
121
  // 떼어 두어 `--no-record`/hook처럼 presence 기록 함수에 도달하지 않는 경로도 후속 child에 유출하지 않는다.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.36.301",
3
+ "version": "1.36.303",
4
4
  "description": "The AI-coding operations layer that makes \"done\" require evidence — persistent memory, evidence-gated completion checks, and clean handoffs for any AI agent (Claude Code, Codex, Cursor). State lives as plain files in your repo. CLI + MCP, 0 runtime dependencies.",
5
5
  "keywords": [
6
6
  "leerness",
@@ -136,6 +136,21 @@ try {
136
136
  assert.deepStrictEqual(s1.results.map(r => r.key), ['node ./a.js', 'node ./d.js']);
137
137
  });
138
138
 
139
+ check('runNpm keeps LEERNESS_CI_SHARD_TIMES to itself: leaves do not see it, the runner still writes its times', () => {
140
+ // 1.36.303: the full run's times file was overwritten mid-run by this probe's own runNpm, which inherited the
141
+ // variable from the outer runner through the leaf environment.
142
+ const dir = path.join(arena, 'times-env');
143
+ fs.mkdirSync(dir);
144
+ const seen = path.join(dir, 'seen.txt'), times = path.join(dir, 'times.json');
145
+ fs.writeFileSync(path.join(dir, 'e.js'), `require('fs').writeFileSync(${JSON.stringify(seen)}, Object.keys(process.env).filter(k => k.toUpperCase() === 'LEERNESS_CI_SHARD_TIMES').length ? 'set' : 'unset');\n`);
146
+ const env = { ...process.env, GITHUB_ACTIONS: '', LEERNESS_CI_SHARD_TIMES: times };
147
+ const r = cs.runNpm('suite', { index: 1, count: 1 }, { root: dir, scripts: { suite: 'node ./e.js' }, weights: { e2e: {}, npm: {} }, stdio: 'ignore', env, log: () => {} });
148
+ assert.deepStrictEqual(r.failed, []);
149
+ assert.strictEqual(fs.readFileSync(seen, 'utf8'), 'unset', 'a leaf saw the runner\'s times variable');
150
+ assert.deepStrictEqual(Object.keys(JSON.parse(fs.readFileSync(times, 'utf8'))), ['node ./e.js'], 'the runner did not write its own times');
151
+ assert.strictEqual(env.LEERNESS_CI_SHARD_TIMES, times, 'the caller\'s environment object was changed');
152
+ });
153
+
139
154
  const CREATE = names => `const e2eShard = require('./ci-shard').e2eShard(__filename, [${names.map(n => `'${n}'`).join(', ')}]);`;
140
155
  const START = '// e2e-shard: cases start here';
141
156
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "_about": "Seconds per unit, for scripts/ci-shard.js. e2e: per case of scripts/e2e.js, from the result-line timestamps of the Windows Node 18 gate job of public CI run 38193803239 (1.36.299). npm: per leaf of test:commands and test:fast, measured on a Windows PC with Node 26.3.0 (2026-10-13), with other test runs in parallel. Weights only balance the shards; a missing or stale weight cannot drop a unit. Refresh: node ./scripts/ci-shard.js weights.",
2
+ "_about": "Seconds per unit, for scripts/ci-shard.js. e2e: per case of scripts/e2e.js, from the result-line timestamps of the Windows Node 18 gate job of public CI run 38193803239 (1.36.299); kept in 1.36.302 because no weighting from the first sharded run (38481240201) lowered the slowest Windows e2e shard (Node 18, 60.5 min) and most raised it (T-0360). npm: per leaf of test:commands and test:fast, the median of the Windows fast and gate commands jobs of public CI run 38481240201 (1.36.301; ci-shard: lines). Weights only balance the shards; a missing or stale weight cannot drop a unit. Refresh: node ./scripts/ci-shard.js weights.",
3
3
  "e2e": {
4
4
  "--help: 제거된 설정 명령 없음": 0.2,
5
5
  "A(1.36.103)": 16.2,
@@ -424,167 +424,168 @@
424
424
  "제거된 setup-agents 명령은 설정을 변경하지 않음": 0.2
425
425
  },
426
426
  "npm": {
427
- "node ./scripts/agents-bench-output-probe.js": 8.9,
428
- "node ./scripts/api-skill-match-probe.js": 314.2,
429
- "node ./scripts/auto-update-hooks-probe.js": 21.6,
430
- "node ./scripts/bugfix-areas-probe.js": 184.9,
431
- "node ./scripts/claim-runtime-cache-probe.js": 4.5,
432
- "node ./scripts/claim-sweep-git-probe.js": 9.7,
433
- "node ./scripts/claims-baseline-concurrency-probe.js": 26.3,
434
- "node ./scripts/claims-baseline-probe.js": 37.4,
435
- "node ./scripts/command-flags-probe.js": 33.9,
436
- "node ./scripts/copy-tree-probe.js": 0.3,
437
- "node ./scripts/crud-check-probe.js": 168.3,
438
- "node ./scripts/dead-flags-probe.js": 68.8,
439
- "node ./scripts/decision-fence-offset-probe.js": 0.4,
440
- "node ./scripts/done-status-probe.js": 222.4,
441
- "node ./scripts/dry-run-refusal-probe.js": 79,
427
+ "node ./scripts/agents-bench-output-probe.js": 7,
428
+ "node ./scripts/api-skill-match-probe.js": 213,
429
+ "node ./scripts/auto-update-hooks-probe.js": 24.1,
430
+ "node ./scripts/bugfix-areas-probe.js": 127.8,
431
+ "node ./scripts/ci-shard-probe.js": 1.4,
432
+ "node ./scripts/claim-runtime-cache-probe.js": 3.3,
433
+ "node ./scripts/claim-sweep-git-probe.js": 7.2,
434
+ "node ./scripts/claims-baseline-concurrency-probe.js": 15.1,
435
+ "node ./scripts/claims-baseline-probe.js": 22.5,
436
+ "node ./scripts/command-flags-probe.js": 19,
437
+ "node ./scripts/copy-tree-probe.js": 0.1,
438
+ "node ./scripts/crud-check-probe.js": 129.9,
439
+ "node ./scripts/dead-flags-probe.js": 36.1,
440
+ "node ./scripts/decision-fence-offset-probe.js": 0.6,
441
+ "node ./scripts/done-status-probe.js": 125.9,
442
+ "node ./scripts/dry-run-refusal-probe.js": 56.6,
442
443
  "node ./scripts/e2e-child-diagnostics-probe.js": 0.5,
443
- "node ./scripts/e2e-command-surface.js": 48.9,
444
+ "node ./scripts/e2e-command-surface.js": 30.5,
444
445
  "node ./scripts/e2e-repo-guard.js": 0.1,
445
- "node ./scripts/e2e-temp-scope-probe.js": 0.3,
446
+ "node ./scripts/e2e-temp-scope-probe.js": 0.2,
446
447
  "node ./scripts/encoding-selftest-probe.js": 0.5,
447
- "node ./scripts/execution-ledger-sync-probe.js": 7.7,
448
- "node ./scripts/false-claim-probe.js": 156.5,
449
- "node ./scripts/file-lease-probe.js": 256.9,
450
- "node ./scripts/fix-evidence-language-probe.js": 130.7,
451
- "node ./scripts/free-text-args-probe.js": 113.9,
452
- "node ./scripts/git-resolution-cache-probe.js": 1.5,
453
- "node ./scripts/handoff-i18n-probe.js": 194.4,
454
- "node ./scripts/handoff-preservation-probe.js": 34.7,
455
- "node ./scripts/harness-text-probe.js": 68.9,
456
- "node ./scripts/i18n-next-cluster-probe.js": 104.8,
457
- "node ./scripts/i18n-priority-surface-probe.js": 32.2,
458
- "node ./scripts/lazy-auto-track-probe.js": 41.7,
459
- "node ./scripts/lint.js": 42.7,
460
- "node ./scripts/lock-heartbeat-probe.js": 10.3,
461
- "node ./scripts/lock-probe.js": 0.1,
462
- "node ./scripts/mcp-presence-probe.js": 102.2,
463
- "node ./scripts/memory-archive-check-probe.js": 140.7,
464
- "node ./scripts/memory-archive-retention-probe.js": 192,
465
- "node ./scripts/memory-arena-cleanup-probe.js": 0.8,
466
- "node ./scripts/memory-candidate-budget-probe.js": 0.1,
448
+ "node ./scripts/execution-ledger-sync-probe.js": 3.6,
449
+ "node ./scripts/false-claim-probe.js": 131.1,
450
+ "node ./scripts/file-lease-probe.js": 97.6,
451
+ "node ./scripts/fix-evidence-language-probe.js": 73.3,
452
+ "node ./scripts/free-text-args-probe.js": 79.7,
453
+ "node ./scripts/git-resolution-cache-probe.js": 0.2,
454
+ "node ./scripts/handoff-i18n-probe.js": 146.8,
455
+ "node ./scripts/handoff-preservation-probe.js": 19.2,
456
+ "node ./scripts/harness-text-probe.js": 34.1,
457
+ "node ./scripts/i18n-next-cluster-probe.js": 41.7,
458
+ "node ./scripts/i18n-priority-surface-probe.js": 19.3,
459
+ "node ./scripts/lazy-auto-track-probe.js": 33.2,
460
+ "node ./scripts/lint.js": 21.6,
461
+ "node ./scripts/lock-heartbeat-probe.js": 10.1,
462
+ "node ./scripts/lock-probe.js": 0,
463
+ "node ./scripts/mcp-presence-probe.js": 76.5,
464
+ "node ./scripts/memory-archive-check-probe.js": 105.4,
465
+ "node ./scripts/memory-archive-retention-probe.js": 139.6,
466
+ "node ./scripts/memory-arena-cleanup-probe.js": 0.3,
467
+ "node ./scripts/memory-candidate-budget-probe.js": 0.2,
467
468
  "node ./scripts/memory-candidate-isolation-probe.js": 0.1,
468
- "node ./scripts/memory-checked-input-probe.js": 85.5,
469
- "node ./scripts/memory-command-policy-mcp-probe.js": 32.4,
470
- "node ./scripts/memory-command-policy-probe.js": 79.4,
471
- "node ./scripts/memory-command-size-probe.js": 0.2,
472
- "node ./scripts/memory-footprint-probe.js": 17.9,
473
- "node ./scripts/memory-footprint-race-probe.js": 8.5,
474
- "node ./scripts/memory-guard-order-probe.js": 50.7,
475
- "node ./scripts/memory-input-preflight-probe.js": 75.3,
469
+ "node ./scripts/memory-checked-input-probe.js": 72,
470
+ "node ./scripts/memory-command-policy-mcp-probe.js": 21.3,
471
+ "node ./scripts/memory-command-policy-probe.js": 36.4,
472
+ "node ./scripts/memory-command-size-probe.js": 0.1,
473
+ "node ./scripts/memory-footprint-probe.js": 7,
474
+ "node ./scripts/memory-footprint-race-probe.js": 5.3,
475
+ "node ./scripts/memory-guard-order-probe.js": 39.4,
476
+ "node ./scripts/memory-input-preflight-probe.js": 65,
476
477
  "node ./scripts/memory-input-unit-probe.js": 0.2,
477
- "node ./scripts/memory-item-admission-probe.js": 36.2,
478
+ "node ./scripts/memory-item-admission-probe.js": 18,
478
479
  "node ./scripts/memory-items-unit-probe.js": 0.1,
479
- "node ./scripts/memory-language-probe.js": 266.3,
480
- "node ./scripts/memory-legacy-retention-probe.js": 66.1,
481
- "node ./scripts/memory-migration-boundary-probe.js": 19.7,
482
- "node ./scripts/memory-migration-concurrency-probe.js": 16.6,
483
- "node ./scripts/memory-migration-failure-probe.js": 89.1,
484
- "node ./scripts/memory-migration-isolation-probe.js": 0.3,
485
- "node ./scripts/memory-migration-observe-probe.js": 19.8,
486
- "node ./scripts/memory-migration-plan-probe.js": 0.3,
487
- "node ./scripts/memory-migration-race-probe.js": 4.3,
488
- "node ./scripts/memory-migration-transport-probe.js": 27.7,
489
- "node ./scripts/memory-migration-write-probe.js": 45.2,
490
- "node ./scripts/memory-output-parent-race-probe.js": 0.2,
480
+ "node ./scripts/memory-language-probe.js": 185.3,
481
+ "node ./scripts/memory-legacy-retention-probe.js": 42.4,
482
+ "node ./scripts/memory-migration-boundary-probe.js": 11.8,
483
+ "node ./scripts/memory-migration-concurrency-probe.js": 13.9,
484
+ "node ./scripts/memory-migration-failure-probe.js": 34.8,
485
+ "node ./scripts/memory-migration-isolation-probe.js": 0.1,
486
+ "node ./scripts/memory-migration-observe-probe.js": 6.6,
487
+ "node ./scripts/memory-migration-plan-probe.js": 0.8,
488
+ "node ./scripts/memory-migration-race-probe.js": 1.9,
489
+ "node ./scripts/memory-migration-transport-probe.js": 14.5,
490
+ "node ./scripts/memory-migration-write-probe.js": 44.4,
491
+ "node ./scripts/memory-output-parent-race-probe.js": 0.1,
491
492
  "node ./scripts/memory-output-posix-probe.js": 0.1,
492
- "node ./scripts/memory-output-publication-probe.js": 27.5,
493
- "node ./scripts/memory-output-unit-probe.js": 0.4,
494
- "node ./scripts/memory-policy-root-spelling-probe.js": 31.5,
495
- "node ./scripts/memory-projection-retention-probe.js": 30,
496
- "node ./scripts/memory-publication-cli-probe.js": 85.2,
497
- "node ./scripts/memory-publication-policy-probe.js": 38.6,
498
- "node ./scripts/memory-recovery-external-probe.js": 6.8,
499
- "node ./scripts/memory-recovery-receipt-probe.js": 23.6,
500
- "node ./scripts/memory-recovery-transfer-probe.js": 1.4,
501
- "node ./scripts/memory-restore-canonical-probe.js": 79.1,
502
- "node ./scripts/memory-utf8-admission-probe.js": 85.8,
503
- "node ./scripts/memory-write-admission-probe.js": 50.5,
504
- "node ./scripts/memory-write-budget-cli-probe.js": 20.5,
505
- "node ./scripts/memory-write-budget-probe.js": 33.1,
506
- "node ./scripts/memory-write-budget-race-probe.js": 7.7,
507
- "node ./scripts/memory-write-dry-run-probe.js": 7.1,
508
- "node ./scripts/memory-write-preparation-cli-probe.js": 15.7,
509
- "node ./scripts/memory-write-preparation-probe.js": 22.3,
510
- "node ./scripts/mutation-integrity-probe.js": 63.5,
511
- "node ./scripts/next-action-suggestion-probe.js": 8,
512
- "node ./scripts/openai-response-retrieval-probe.js": 1.2,
513
- "node ./scripts/openai-ui-review-dispatch-probe.js": 878.7,
514
- "node ./scripts/openai-ui-review-observation-probe.js": 1035.8,
515
- "node ./scripts/openai-ui-review-observation-record-probe.js": 0.2,
516
- "node ./scripts/openai-ui-review-receipt-probe.js": 548.3,
493
+ "node ./scripts/memory-output-publication-probe.js": 18.3,
494
+ "node ./scripts/memory-output-unit-probe.js": 0.3,
495
+ "node ./scripts/memory-policy-root-spelling-probe.js": 16.5,
496
+ "node ./scripts/memory-projection-retention-probe.js": 21.3,
497
+ "node ./scripts/memory-publication-cli-probe.js": 41,
498
+ "node ./scripts/memory-publication-policy-probe.js": 29,
499
+ "node ./scripts/memory-recovery-external-probe.js": 4.8,
500
+ "node ./scripts/memory-recovery-receipt-probe.js": 15.5,
501
+ "node ./scripts/memory-recovery-transfer-probe.js": 0.9,
502
+ "node ./scripts/memory-restore-canonical-probe.js": 59.6,
503
+ "node ./scripts/memory-utf8-admission-probe.js": 71.9,
504
+ "node ./scripts/memory-write-admission-probe.js": 19.9,
505
+ "node ./scripts/memory-write-budget-cli-probe.js": 15.1,
506
+ "node ./scripts/memory-write-budget-probe.js": 21.7,
507
+ "node ./scripts/memory-write-budget-race-probe.js": 6.2,
508
+ "node ./scripts/memory-write-dry-run-probe.js": 4.6,
509
+ "node ./scripts/memory-write-preparation-cli-probe.js": 6.8,
510
+ "node ./scripts/memory-write-preparation-probe.js": 23.8,
511
+ "node ./scripts/mutation-integrity-probe.js": 26.3,
512
+ "node ./scripts/next-action-suggestion-probe.js": 5.2,
513
+ "node ./scripts/openai-response-retrieval-probe.js": 2.1,
514
+ "node ./scripts/openai-ui-review-dispatch-probe.js": 522.2,
515
+ "node ./scripts/openai-ui-review-observation-probe.js": 331,
516
+ "node ./scripts/openai-ui-review-observation-record-probe.js": 0.1,
517
+ "node ./scripts/openai-ui-review-receipt-probe.js": 324,
517
518
  "node ./scripts/parent-detect-selftest-probe.js": 0.2,
518
- "node ./scripts/path-setup-powershell-probe.js": 29.5,
519
- "node ./scripts/plain-language-reporting-probe.js": 20.6,
520
- "node ./scripts/platform-smoke.js": 45.3,
521
- "node ./scripts/provider-capacity-probe.js": 25.1,
522
- "node ./scripts/race-overlap-probe.js": 25.4,
523
- "node ./scripts/release-runtime-probe.js": 80.3,
524
- "node ./scripts/review-checkpoint-probe.js": 0.2,
525
- "node ./scripts/review-checkpoint-transport-probe.js": 7.6,
526
- "node ./scripts/review-request-labels-probe.js": 34.7,
527
- "node ./scripts/review-request-signals-probe.js": 54.7,
528
- "node ./scripts/review-snapshot-probe.js": 0.3,
529
- "node ./scripts/review-snapshot-transport-probe.js": 9.5,
530
- "node ./scripts/role-agent-schema-probe.js": 0.5,
531
- "node ./scripts/role-fallback-probe.js": 96.7,
532
- "node ./scripts/role-file-identity-probe.js": 19.8,
533
- "node ./scripts/role-store-loader-probe.js": 61.3,
534
- "node ./scripts/rule-applicability-probe.js": 2.5,
535
- "node ./scripts/rule-verification-probe.js": 27.2,
536
- "node ./scripts/runtime-admission-probe.js": 3.8,
537
- "node ./scripts/runtime-cost-probe.js": 135,
538
- "node ./scripts/runtime-descriptor-identity-probe.js": 6.5,
519
+ "node ./scripts/path-setup-powershell-probe.js": 13.6,
520
+ "node ./scripts/plain-language-reporting-probe.js": 6,
521
+ "node ./scripts/platform-smoke.js": 36.2,
522
+ "node ./scripts/provider-capacity-probe.js": 13.8,
523
+ "node ./scripts/race-overlap-probe.js": 24.6,
524
+ "node ./scripts/release-runtime-probe.js": 60.7,
525
+ "node ./scripts/review-checkpoint-probe.js": 0.1,
526
+ "node ./scripts/review-checkpoint-transport-probe.js": 3.5,
527
+ "node ./scripts/review-request-labels-probe.js": 18,
528
+ "node ./scripts/review-request-signals-probe.js": 33.3,
529
+ "node ./scripts/review-snapshot-probe.js": 0.2,
530
+ "node ./scripts/review-snapshot-transport-probe.js": 8.5,
531
+ "node ./scripts/role-agent-schema-probe.js": 0.6,
532
+ "node ./scripts/role-fallback-probe.js": 85.7,
533
+ "node ./scripts/role-file-identity-probe.js": 21.1,
534
+ "node ./scripts/role-store-loader-probe.js": 59.6,
535
+ "node ./scripts/rule-applicability-probe.js": 2.6,
536
+ "node ./scripts/rule-verification-probe.js": 7.3,
537
+ "node ./scripts/runtime-admission-probe.js": 4.2,
538
+ "node ./scripts/runtime-cost-probe.js": 77.1,
539
+ "node ./scripts/runtime-descriptor-identity-probe.js": 4.9,
539
540
  "node ./scripts/runtime-git-write-probe.js": 0.1,
540
- "node ./scripts/runtime-layout-probe.js": 240.2,
541
- "node ./scripts/runtime-repl-probe.js": 9.7,
542
- "node ./scripts/runtime-replacement-probe.js": 23.7,
543
- "node ./scripts/runtime-write-probe.js": 46.5,
544
- "node ./scripts/secret-scan-scope-probe.js": 2.5,
545
- "node ./scripts/selftest-cleanup-probe.js": 21.2,
546
- "node ./scripts/shared-state-lock-probe.js": 5.1,
547
- "node ./scripts/skill-archive-probe.js": 1.8,
548
- "node ./scripts/smoke.js": 25.5,
549
- "node ./scripts/state-inspect-cli-probe.js": 9.4,
550
- "node ./scripts/state-scopes-probe.js": 41,
541
+ "node ./scripts/runtime-layout-probe.js": 84.8,
542
+ "node ./scripts/runtime-repl-probe.js": 5.5,
543
+ "node ./scripts/runtime-replacement-probe.js": 18.3,
544
+ "node ./scripts/runtime-write-probe.js": 32.1,
545
+ "node ./scripts/secret-scan-scope-probe.js": 1.6,
546
+ "node ./scripts/selftest-cleanup-probe.js": 17.7,
547
+ "node ./scripts/shared-state-lock-probe.js": 4.5,
548
+ "node ./scripts/skill-archive-probe.js": 1.1,
549
+ "node ./scripts/smoke.js": 17.7,
550
+ "node ./scripts/state-inspect-cli-probe.js": 4.2,
551
+ "node ./scripts/state-scopes-probe.js": 6.7,
551
552
  "node ./scripts/store-diagnostic-identity-probe.js": 1,
552
- "node ./scripts/store-diagnostic-probe.js": 5.7,
553
- "node ./scripts/store-values-probe.js": 144.7,
554
- "node ./scripts/task-sync-roundtrip-probe.js": 437.8,
555
- "node ./scripts/temp-cleanup-probe.js": 2.8,
556
- "node ./scripts/test-execution-plan-probe.js": 1.2,
557
- "node ./scripts/test-firestore-probe.js": 3.4,
558
- "node ./scripts/test-http-probe.js": 3.9,
559
- "node ./scripts/test-node-runner-probe.js": 14.9,
560
- "node ./scripts/test-npm-script-probe.js": 2.7,
561
- "node ./scripts/test-npm-script-runner-probe.js": 12.8,
562
- "node ./scripts/test-prerequisite-probe.js": 2.4,
563
- "node ./scripts/test-suite-list-runner-probe.js": 17.5,
564
- "node ./scripts/title-args-probe.js": 156.2,
565
- "node ./scripts/tracker-write-language-probe.js": 115.4,
566
- "node ./scripts/ui-boundary-browser-probe.js": 0.2,
567
- "node ./scripts/ui-boundary-contracts-probe.js": 5.3,
553
+ "node ./scripts/store-diagnostic-probe.js": 2.2,
554
+ "node ./scripts/store-values-probe.js": 82.8,
555
+ "node ./scripts/task-sync-roundtrip-probe.js": 264.2,
556
+ "node ./scripts/temp-cleanup-probe.js": 2.7,
557
+ "node ./scripts/test-execution-plan-probe.js": 0.6,
558
+ "node ./scripts/test-firestore-probe.js": 2.7,
559
+ "node ./scripts/test-http-probe.js": 2.1,
560
+ "node ./scripts/test-node-runner-probe.js": 13.9,
561
+ "node ./scripts/test-npm-script-probe.js": 1.4,
562
+ "node ./scripts/test-npm-script-runner-probe.js": 6.6,
563
+ "node ./scripts/test-prerequisite-probe.js": 1,
564
+ "node ./scripts/test-suite-list-runner-probe.js": 8.9,
565
+ "node ./scripts/title-args-probe.js": 114.4,
566
+ "node ./scripts/tracker-write-language-probe.js": 66.4,
567
+ "node ./scripts/ui-boundary-browser-probe.js": 0.3,
568
+ "node ./scripts/ui-boundary-contracts-probe.js": 2.5,
568
569
  "node ./scripts/ui-boundary-probe.js": 0.1,
569
- "node ./scripts/ui-boundary-scanner-probe.js": 0.2,
570
+ "node ./scripts/ui-boundary-scanner-probe.js": 0.1,
570
571
  "node ./scripts/ui-boundary-trials-probe.js": 0.1,
571
- "node ./scripts/ui-copy-boundary-probe.js": 40,
572
- "node ./scripts/ui-information-quality-probe.js": 0.6,
573
- "node ./scripts/ui-review-ledger-probe.js": 68.5,
574
- "node ./scripts/ui-review-observation-probe.js": 2.3,
575
- "node ./scripts/ui-review-probe.js": 0.3,
572
+ "node ./scripts/ui-copy-boundary-probe.js": 12.7,
573
+ "node ./scripts/ui-information-quality-probe.js": 0.4,
574
+ "node ./scripts/ui-review-ledger-probe.js": 27.7,
575
+ "node ./scripts/ui-review-observation-probe.js": 2.2,
576
+ "node ./scripts/ui-review-probe.js": 0.4,
576
577
  "node ./scripts/ui-review-provider-receipt-probe.js": 0.2,
577
- "node ./scripts/ui-review-recovery-probe.js": 602,
578
- "node ./scripts/ui-review-transport-probe.js": 22.4,
578
+ "node ./scripts/ui-review-recovery-probe.js": 342.7,
579
+ "node ./scripts/ui-review-transport-probe.js": 16.8,
579
580
  "node ./scripts/which-shim-probe.js": 0.1,
580
- "node ./scripts/workspace-dir-lock-order-probe.js": 5.9,
581
- "node ./scripts/workspace-dir-migration-probe.js": 27.7,
582
- "node ./scripts/workspace-dir-source-ratchet.js": 0.3,
583
- "node ./scripts/workspace-migration-heartbeat-probe.js": 36.8,
584
- "node ./scripts/workspace-selection-probe.js": 0.3,
585
- "node ./scripts/write-path-language-probe.js": 88.9,
586
- "node ./scripts/write-primitive-language-probe.js": 34.9,
587
- "node scripts/history-retention-probe.js": 11.6,
588
- "node scripts/history-retention-write-probe.js": 80.7
581
+ "node ./scripts/workspace-dir-lock-order-probe.js": 2,
582
+ "node ./scripts/workspace-dir-migration-probe.js": 23.1,
583
+ "node ./scripts/workspace-dir-source-ratchet.js": 0.1,
584
+ "node ./scripts/workspace-migration-heartbeat-probe.js": 97.8,
585
+ "node ./scripts/workspace-selection-probe.js": 0.2,
586
+ "node ./scripts/write-path-language-probe.js": 63.7,
587
+ "node ./scripts/write-primitive-language-probe.js": 29.4,
588
+ "node scripts/history-retention-probe.js": 11.8,
589
+ "node scripts/history-retention-write-probe.js": 70.2
589
590
  }
590
591
  }
@@ -137,12 +137,15 @@ function runNpm(name, spec, opts = {}) {
137
137
  const mine = leaves.filter(l => l.shard === spec.index);
138
138
  const log = opts.log || (line => process.stdout.write(line + '\n'));
139
139
  const grouped = env.GITHUB_ACTIONS === 'true';
140
+ // Leaves do not see this runner's times file: a leaf that runs runNpm itself (ci-shard-probe.js) would overwrite it.
141
+ const leafEnv = { ...env };
142
+ for (const k of Object.keys(leafEnv)) if (k.toUpperCase() === 'LEERNESS_CI_SHARD_TIMES') delete leafEnv[k];
140
143
  log(`ci-shard: ${name} shard ${spec.index}/${spec.count} runs ${mine.length} of ${leaves.length} leaves`);
141
144
  const results = [];
142
145
  for (const leaf of mine) {
143
146
  if (grouped) log(`::group::${leaf.key}`);
144
147
  const started = Date.now();
145
- const r = cp.spawnSync(process.execPath, [path.resolve(root, leaf.file), ...leaf.args], { cwd: root, stdio: opts.stdio || 'inherit', env });
148
+ const r = cp.spawnSync(process.execPath, [path.resolve(root, leaf.file), ...leaf.args], { cwd: root, stdio: opts.stdio || 'inherit', env: leafEnv });
146
149
  const seconds = (Date.now() - started) / 1000;
147
150
  const ok = r.status === 0 && !r.signal && !r.error;
148
151
  results.push({ key: leaf.key, ok, seconds, status: r.status, signal: r.signal || null, error: r.error ? String(r.error.code || r.error.message) : null });
package/scripts/e2e.js CHANGED
@@ -1356,7 +1356,8 @@ if (e2eShard.take('B(1.9.48)')) {
1356
1356
  // 1.9.48 cross-platform archive — PowerShell ZIP or tar
1357
1357
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-arc-'));
1358
1358
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'all'], { stdio: 'ignore', timeout: 30000 });
1359
- const r = cp.spawnSync(process.execPath, [CLI, 'skill', 'publish', '--path', tmpC, '--bundle-only'], { encoding: 'utf8', timeout: 30000 });
1359
+ // T-0358: the publish pre-check reads drift, which turns stale at UTC midnight; store-values-probe tests the pre-check.
1360
+ const r = cp.spawnSync(process.execPath, [CLI, 'skill', 'publish', '--path', tmpC, '--bundle-only', '--no-security-check'], { encoding: 'utf8', timeout: 30000 });
1360
1361
  const tarballDir = path.join(tmpC, '.leerness', 'skills-publish-tarball');
1361
1362
  const files = fs.existsSync(tarballDir) ? fs.readdirSync(tarballDir) : [];
1362
1363
  const archive = files.find(f => /\.(tgz|zip)$/.test(f));
@@ -1425,7 +1426,8 @@ if (e2eShard.take('B(1.9.47)')) {
1425
1426
  total++;
1426
1427
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-pub-'));
1427
1428
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'all'], { stdio: 'ignore', timeout: 30000 });
1428
- const r = cp.spawnSync(process.execPath, [CLI, 'skill', 'publish', '--path', tmpC, '--bundle-only'], { encoding: 'utf8', timeout: 30000 });
1429
+ // T-0358: as above, the pre-check has its own date-independent test in store-values-probe.
1430
+ const r = cp.spawnSync(process.execPath, [CLI, 'skill', 'publish', '--path', tmpC, '--bundle-only', '--no-security-check'], { encoding: 'utf8', timeout: 30000 });
1429
1431
  const publishDir = path.join(tmpC, '.leerness', 'skills-publish');
1430
1432
  const manifestFile = path.join(publishDir, 'manifest.json');
1431
1433
  let manifest = null;
@@ -58,6 +58,18 @@ const skill = (dir, id, fields) => {
58
58
  fs.writeFileSync(store(dir, 'skills', id, 'skill.json'), JSON.stringify({ name: id, ...fields }, null, 2) + '\n');
59
59
  };
60
60
  const constraints = (dir, platforms) => fs.writeFileSync(store(dir, 'platform-constraints.json'), JSON.stringify({ platforms }, null, 2) + '\n');
61
+ // T-0358: skill publish first runs a pre-check (health), which fails while drift needs attention, and drift counts a
62
+ // tracker last dated the previous UTC day as stale. The template is made once at the start, so a run that crossed
63
+ // UTC midnight before a publish test failed there. The tests about generated text skip the pre-check; the pre-check
64
+ // has its own test below, with dates that do not depend on the time of day.
65
+ const NO_PRECHECK = ['--no-security-check'];
66
+ const trackerDated = (dir, daysFromToday) => {
67
+ const day = new Date(Date.now() + daysFromToday * 86400000).toISOString().slice(0, 10);
68
+ const file = store(dir, 'progress-tracker.md');
69
+ const text = fs.readFileSync(file, 'utf8');
70
+ assert.ok(/\d{4}-\d{2}-\d{2}/.test(text), 'the template tracker holds no date');
71
+ fs.writeFileSync(file, text.replace(/\d{4}-\d{2}-\d{2}/g, day));
72
+ };
61
73
  // What a non-text value turned into: an object, a comma-joined array, a missing value.
62
74
  const BAD = /\[object Object\]|undefined|얼룩말,목록/;
63
75
  const JS_ERROR = /is not a function|Cannot read properties|is not iterable|of undefined|of null/;
@@ -107,7 +119,7 @@ test('array fields that are not arrays do not crash skill commands, and skill le
107
119
  skill(dir, 'str-items', { displayNameKo: '문자열 항목', capabilities: ['zebra export'], patterns: ['run it'], optimizations: ['faster'], sources: [{ note: 'no url' }, null, { url: '' }], verification: null });
108
120
  skill(dir, 'good-sources', { displayNameKo: '출처 있음', capabilities: ['zebra export'], sources: ['https://string.example', { url: 'https://object.example' }] });
109
121
  for (const args of [['skill', 'search', 'zebra'], ['skill', 'info', 'str-caps'], ['skill', 'info', 'obj-caps'], ['skill', 'info', 'str-items'], ['skill', 'consolidate'],
110
- ['skill', 'export', 'str-caps'], ['skill', 'export', 'obj-caps'], ['skill', 'export', 'str-items'], ['skill', 'export', 'good-sources'], ['skill', 'export-all'], ['skill', 'publish'],
122
+ ['skill', 'export', 'str-caps'], ['skill', 'export', 'obj-caps'], ['skill', 'export', 'str-items'], ['skill', 'export', 'good-sources'], ['skill', 'export-all'], ['skill', 'publish', ...NO_PRECHECK],
111
123
  ['skill', 'learn', 'obj-caps'], ['skill', 'learn', 'str-items'], ['skill', 'learn', 'str-items', '--doc', 'https://kite.example'], ['skill', 'learn', 'good-sources']]) {
112
124
  clean(must(run(dir, [...args, '--path', dir]), args.join(' ')), args.join(' '));
113
125
  }
@@ -225,7 +237,7 @@ test('generated files hold text only, and the export description still prefers d
225
237
  const dir = project();
226
238
  for (const [id, fields] of Object.entries(SKILLS)) skill(dir, id, fields);
227
239
  skill(dir, 'described', { displayNameKo: '설명 있음', description: 'Use when drawing zebra charts', capabilities: ['zebra export'] });
228
- for (const args of [['skill', 'export', 'obj-desc'], ['skill', 'export-all'], ['skill', 'publish'], ['skill', 'learn', 'obj-name'], ['skill', 'learn', 'objitem-caps'],
240
+ for (const args of [['skill', 'export', 'obj-desc'], ['skill', 'export-all'], ['skill', 'publish', ...NO_PRECHECK], ['skill', 'learn', 'obj-name'], ['skill', 'learn', 'objitem-caps'],
229
241
  ['roadmap'], ['skill', 'match', 'zebra']]) must(run(dir, [...args, '--path', dir]), args.join(' '));
230
242
  // Only what these commands write: exported/published bundles, skill READMEs, the roadmap page, the match history.
231
243
  const files = [path.join(dir, 'roadmap.html'), store(dir, 'skill-suggestions.md')].filter(f => fs.existsSync(f));
@@ -242,6 +254,21 @@ test('generated files hold text only, and the export description still prefers d
242
254
  assert.deepStrictEqual(manifest.skills.filter(s => s.id === 'obj-name').map(s => s.name), ['obj-name']);
243
255
  });
244
256
 
257
+ test('skill publish stops while drift needs attention, --no-security-check skips that check (no clock dependency)', () => {
258
+ // A tracker dated two days back is stale at any time of day; one dated tomorrow is not.
259
+ const stale = project(), fresh = project();
260
+ for (const dir of [stale, fresh]) skill(dir, 'plain', { displayNameKo: '기본', description: 'Use when drawing zebra charts', capabilities: ['zebra export'] });
261
+ trackerDated(stale, -2); trackerDated(fresh, 1);
262
+ const refused = run(stale, ['skill', 'publish', '--path', stale]);
263
+ assert.strictEqual(refused.status, 1, both(refused));
264
+ assert.ok(/보안 사전 점검 \(1\.9\.98\): \d+건 issue/.test(refused.stdout) && /⚠ drift/.test(refused.stdout), both(refused));
265
+ assert.ok(!fs.existsSync(store(stale, 'skills-publish', 'manifest.json')), 'a refused publish wrote its bundle');
266
+ must(run(stale, ['skill', 'publish', ...NO_PRECHECK, '--path', stale]), 'publish --no-security-check');
267
+ assert.ok(fs.existsSync(store(stale, 'skills-publish', 'manifest.json')), '--no-security-check did not publish');
268
+ const passedCheck = must(run(fresh, ['skill', 'publish', '--path', fresh]), 'publish with a fresh tracker');
269
+ assert.ok(/보안 사전 점검 \(1\.9\.98\): 통과/.test(passedCheck), passedCheck);
270
+ });
271
+
245
272
  test('--json keeps the stored values', () => {
246
273
  const dir = project();
247
274
  for (const [id, fields] of Object.entries(SKILLS)) skill(dir, id, fields);