@open-agent-toolkit/cli 0.3.16 → 0.3.18
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/assets/agents/oat-phase-implementer.md +26 -6
- package/assets/agents/oat-reviewer.md +51 -1
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/contributing/documentation.md +4 -0
- package/assets/docs/contributing/skills.md +3 -1
- package/assets/docs/getting-started/bootstrap.md +1 -1
- package/assets/docs/getting-started/index.md +0 -4
- package/assets/docs/getting-started/tool-packs.md +0 -7
- package/assets/docs/reference/cli-reference.md +42 -1
- package/assets/docs/reference/config-and-local-state.md +11 -2
- package/assets/docs/reference/project-artifacts.md +43 -8
- package/assets/docs/skills/repo-improve.md +9 -7
- package/assets/docs/workflows/advanced/autonomy.md +40 -0
- package/assets/docs/workflows/backlog-and-planning/backlog-lifecycle.md +25 -24
- package/assets/docs/workflows/backlog-and-planning/remote-project-management.md +0 -7
- package/assets/docs/workflows/ideas/lifecycle.md +2 -7
- package/assets/docs/workflows/projects/execution/project-log.md +22 -0
- package/assets/docs/workflows/projects/reviews/review-flavors.md +8 -0
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-agent-instructions-apply/SKILL.md +14 -4
- package/assets/skills/oat-brainstorm/SKILL.md +17 -13
- package/assets/skills/oat-docs-apply/SKILL.md +13 -3
- package/assets/skills/oat-doctor/SKILL.md +11 -1
- package/assets/skills/oat-explainer-kit/SKILL.md +1 -1
- package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +6 -3
- package/assets/skills/oat-pjm-review-backlog/SKILL.md +2 -2
- package/assets/skills/oat-pjm-update-repo-reference/SKILL.md +28 -3
- package/assets/skills/oat-project-autonomous/SKILL.md +60 -5
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +44 -6
- package/assets/skills/oat-project-capture/SKILL.md +7 -3
- package/assets/skills/oat-project-complete/SKILL.md +39 -34
- package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +106 -28
- package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +30 -3
- package/assets/skills/oat-project-design/SKILL.md +12 -9
- package/assets/skills/oat-project-discover/SKILL.md +8 -4
- package/assets/skills/oat-project-document/SKILL.md +14 -5
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +44 -6
- package/assets/skills/oat-project-implement/SKILL.md +1 -1
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +6 -6
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +44 -6
- package/assets/skills/oat-project-implement/references/phase-execution.md +20 -6
- package/assets/skills/oat-project-import-plan/SKILL.md +20 -6
- package/assets/skills/oat-project-lite/SKILL.md +7 -5
- package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +44 -6
- package/assets/skills/oat-project-new/SKILL.md +6 -2
- package/assets/skills/oat-project-plan/SKILL.md +14 -6
- package/assets/skills/oat-project-pr-final/SKILL.md +1 -1
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +44 -6
- package/assets/skills/oat-project-progress/SKILL.md +13 -1
- package/assets/skills/oat-project-promote-spec-driven/SKILL.md +7 -3
- package/assets/skills/oat-project-quick-start/SKILL.md +17 -12
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +44 -6
- package/assets/skills/oat-project-reconcile/SKILL.md +8 -4
- package/assets/skills/oat-project-retro/SKILL.md +1 -1
- package/assets/skills/oat-project-retro/references/apply-procedure.md +6 -6
- package/assets/skills/oat-project-retro-file/SKILL.md +7 -3
- package/assets/skills/oat-project-review-provide/SKILL.md +23 -4
- package/assets/skills/oat-project-review-provide-remote/SKILL.md +19 -1
- package/assets/skills/oat-project-review-receive/SKILL.md +37 -12
- package/assets/skills/oat-project-review-receive-remote/SKILL.md +7 -3
- package/assets/skills/oat-project-revise/SKILL.md +13 -5
- package/assets/skills/oat-project-spec/SKILL.md +8 -4
- package/assets/skills/oat-project-summary/SKILL.md +11 -7
- package/assets/skills/oat-repo-knowledge-index/SKILL.md +54 -20
- package/assets/skills/oat-repo-knowledge-index/scripts/refresh-owned.mjs +118 -0
- package/assets/skills/oat-review-provide/SKILL.md +7 -3
- package/assets/skills/oat-review-receive-remote/SKILL.md +14 -4
- package/assets/skills/oat-wave-execute/SKILL.md +24 -11
- package/assets/skills/oat-wave-execute/assets/wrapper-plan-template.md +16 -2
- package/assets/skills/oat-worktree-bootstrap-auto/SKILL.md +45 -17
- package/assets/templates/pjm-agents.md +38 -8
- package/assets/templates/repo-agents.md +5 -0
- package/assets/templates/repo-readme.md +5 -0
- package/dist/commands/backlog/archive.d.ts +3 -0
- package/dist/commands/backlog/archive.d.ts.map +1 -1
- package/dist/commands/backlog/archive.js +63 -26
- package/dist/commands/backlog/index.d.ts.map +1 -1
- package/dist/commands/backlog/index.js +3 -0
- package/dist/commands/backlog/rewrite-references.d.ts +5 -0
- package/dist/commands/backlog/rewrite-references.d.ts.map +1 -1
- package/dist/commands/backlog/rewrite-references.js +39 -1
- package/dist/commands/init/index.d.ts.map +1 -1
- package/dist/commands/init/index.js +1 -0
- package/dist/commands/init/tools/shared/skill-script-references.d.ts.map +1 -1
- package/dist/commands/init/tools/shared/skill-script-references.js +8 -1
- package/dist/commands/internal/commit-paths.d.ts +3 -0
- package/dist/commands/internal/commit-paths.d.ts.map +1 -0
- package/dist/commands/internal/commit-paths.js +31 -0
- package/dist/commands/internal/index.d.ts.map +1 -1
- package/dist/commands/internal/index.js +2 -0
- package/dist/commands/pjm/init.d.ts.map +1 -1
- package/dist/commands/pjm/init.js +37 -8
- package/dist/commands/project/archive/archive-utils.d.ts +6 -3
- package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
- package/dist/commands/project/archive/archive-utils.js +510 -104
- package/dist/commands/project/archive/push-runner.d.ts.map +1 -1
- package/dist/commands/project/archive/push-runner.js +1 -0
- package/dist/commands/project/log/append.d.ts +2 -3
- package/dist/commands/project/log/append.d.ts.map +1 -1
- package/dist/commands/project/log/append.js +125 -111
- package/dist/commands/project/migrate/index.d.ts +1 -2
- package/dist/commands/project/migrate/index.d.ts.map +1 -1
- package/dist/commands/project/migrate/index.js +8 -2
- package/dist/commands/project/new/scaffold.d.ts.map +1 -1
- package/dist/commands/project/new/scaffold.js +4 -4
- package/dist/commands/project/promote/promote.d.ts +8 -0
- package/dist/commands/project/promote/promote.d.ts.map +1 -1
- package/dist/commands/project/promote/promote.js +37 -6
- package/dist/commands/project/status.d.ts.map +1 -1
- package/dist/commands/project/status.js +3 -0
- package/dist/commands/project/sync/ref-sync.d.ts +12 -1
- package/dist/commands/project/sync/ref-sync.d.ts.map +1 -1
- package/dist/commands/project/sync/ref-sync.js +347 -23
- package/dist/commands/shared/agents-md-lock.d.ts +3 -0
- package/dist/commands/shared/agents-md-lock.d.ts.map +1 -0
- package/dist/commands/shared/agents-md-lock.js +130 -0
- package/dist/commands/shared/agents-md.d.ts.map +1 -1
- package/dist/commands/shared/agents-md.js +26 -8
- package/dist/commands/shared/exact-path-commit.d.ts +32 -0
- package/dist/commands/shared/exact-path-commit.d.ts.map +1 -0
- package/dist/commands/shared/exact-path-commit.js +691 -0
- package/dist/validation/skills.d.ts.map +1 -1
- package/dist/validation/skills.js +138 -11
- package/package.json +3 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push-runner.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/archive/push-runner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAOjE,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAEL,6BAA6B,EAI7B,2BAA2B,EAC3B,sBAAsB,EACtB,oCAAoC,EAEpC,KAAK,iCAAiC,EACtC,KAAK,gCAAgC,EAEtC,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,qCAAqC;IACpD,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;IACtD,2BAA2B,EAAE,OAAO,2BAA2B,CAAC;IAChE,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,oCAAoC,EAAE,OAAO,oCAAoC,CAAC;IAClF,0BAA0B,EAAE,CAC1B,OAAO,EAAE,iCAAiC,KACvC,OAAO,CAAC,gCAAgC,CAAC,CAAC;IAC/C,6BAA6B,EAAE,OAAO,6BAA6B,CAAC;IACpE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,SAAS,EAAE,MAAM,MAAM,CAAC;CACzB;AA0BD,wBAAgB,4CAA4C,IAAI,qCAAqC,CAgBpG;
|
|
1
|
+
{"version":3,"file":"push-runner.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/archive/push-runner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAOjE,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAEL,6BAA6B,EAI7B,2BAA2B,EAC3B,sBAAsB,EACtB,oCAAoC,EAEpC,KAAK,iCAAiC,EACtC,KAAK,gCAAgC,EAEtC,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,qCAAqC;IACpD,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;IACtD,2BAA2B,EAAE,OAAO,2BAA2B,CAAC;IAChE,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,oCAAoC,EAAE,OAAO,oCAAoC,CAAC;IAClF,0BAA0B,EAAE,CAC1B,OAAO,EAAE,iCAAiC,KACvC,OAAO,CAAC,gCAAgC,CAAC,CAAC;IAC/C,6BAA6B,EAAE,OAAO,6BAA6B,CAAC;IACpE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,SAAS,EAAE,MAAM,MAAM,CAAC;CACzB;AA0BD,wBAAgB,4CAA4C,IAAI,qCAAqC,CAgBpG;AA8JD,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,qCAAqC,EACnD,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CAmHf"}
|
|
@@ -111,6 +111,7 @@ function emitArchivePushText(report, summaryExportPath, logger) {
|
|
|
111
111
|
}
|
|
112
112
|
if (report.projectRecapExport) {
|
|
113
113
|
logger.info(`Project recap export: ${report.projectRecapExport.exportRoot}`);
|
|
114
|
+
logger.info(`Recap run: ${report.projectRecapExport.runId}; original ${report.projectRecapExport.page.originalSha256}; exported ${report.projectRecapExport.page.exportedSha256}; verified ${report.projectRecapExport.verifiedArtifactCount} immutable files.`);
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
117
|
function emitArchivePushReport(report, summaryExportPath, context) {
|
|
@@ -132,7 +132,7 @@ export declare function committedProjectLogIdentityState(repoRoot: string, logPa
|
|
|
132
132
|
*
|
|
133
133
|
* The log is tracked, so an uncommitted append leaves the worktree dirty for
|
|
134
134
|
* whatever runs next — including a dispatched subagent whose preflight requires
|
|
135
|
-
* a clean tree. The
|
|
135
|
+
* a clean tree. The shared helper commits exact log paths with enabled hooks so unrelated
|
|
136
136
|
* working-tree changes are never swept in.
|
|
137
137
|
*
|
|
138
138
|
* Scope note: this commits the whole log file, so a log that was already dirty
|
|
@@ -144,8 +144,7 @@ export declare function committedProjectLogIdentityState(repoRoot: string, logPa
|
|
|
144
144
|
* Never throws, and never deletes, moves, or forces an index lock: a lock this
|
|
145
145
|
* process did not take is another process's, and only its owner may clear it.
|
|
146
146
|
* Git failures are reported to the caller, which degrades to a diagnostic
|
|
147
|
-
* rather than altering any exit status.
|
|
148
|
-
* partially staged log is not left behind.
|
|
147
|
+
* rather than altering any exit status. The helper preserves the real index on failure; pending evidence remains resumable.
|
|
149
148
|
*
|
|
150
149
|
* Exactly one outcome is derived from a single pre-action snapshot (entry HEAD
|
|
151
150
|
* plus entry dirtiness) rather than from eligibility re-sampled after the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"append.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/log/append.ts"],"names":[],"mappings":"AA8BA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"append.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/log/append.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@ import { appendFile, readFile, realpath, rm, writeFile, } from 'node:fs/promises
|
|
|
5
5
|
import { homedir, hostname, tmpdir } from 'node:os';
|
|
6
6
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep, } from 'node:path';
|
|
7
7
|
import { buildCommandContext, } from '../../../app/command-context.js';
|
|
8
|
+
import { commitExactPaths } from '../../shared/exact-path-commit.js';
|
|
8
9
|
import { readGlobalOptions } from '../../shared/shared.utils.js';
|
|
9
10
|
import { resolveActiveProject, } from '../../../config/oat-config.js';
|
|
10
11
|
import { resolveEffectiveConfig } from '../../../config/resolve.js';
|
|
@@ -424,14 +425,6 @@ function lockMtimeMs(lockPath) {
|
|
|
424
425
|
return undefined;
|
|
425
426
|
}
|
|
426
427
|
}
|
|
427
|
-
function resolveIndexLockPath(repoRoot) {
|
|
428
|
-
try {
|
|
429
|
-
return join(runGit(repoRoot, ['rev-parse', '--absolute-git-dir']), 'index.lock');
|
|
430
|
-
}
|
|
431
|
-
catch {
|
|
432
|
-
return join(repoRoot, '.git', 'index.lock');
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
428
|
/**
|
|
436
429
|
* Classifies a failed git invocation as an index-lock contention or a real
|
|
437
430
|
* failure.
|
|
@@ -615,103 +608,65 @@ async function commitLockedProjectLog(input, dependencies, run, message) {
|
|
|
615
608
|
}
|
|
616
609
|
return { outcome: 'nothing-to-commit', committed: false, attempts: 0 };
|
|
617
610
|
}
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
catch {
|
|
663
|
-
// Best effort: the reported commit failure already tells the caller
|
|
664
|
-
// the log needs attention.
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
lastError = gitFailureMessage(error);
|
|
668
|
-
if (classifyGitLockFailure(lastError, lockPath) === 'other') {
|
|
669
|
-
// A retry can race a competing writer: once it commits our appended
|
|
670
|
-
// entry and releases the lock, our next `git commit` fails with
|
|
671
|
-
// "nothing to commit", which is settlement rather than a failure.
|
|
672
|
-
// Settlement demands a moved HEAD and a clean log that still carries
|
|
673
|
-
// this entry, so a genuine failure — where the log stays dirty — can
|
|
674
|
-
// never be reclassified by this branch.
|
|
675
|
-
if (settledByAnotherWriter(run, snapshot, input)) {
|
|
676
|
-
return {
|
|
677
|
-
outcome: 'already-committed',
|
|
678
|
-
committed: false,
|
|
679
|
-
attempts: attempt,
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
return {
|
|
683
|
-
outcome: 'failed',
|
|
684
|
-
committed: false,
|
|
685
|
-
attempts: attempt,
|
|
686
|
-
error: lastError,
|
|
687
|
-
lockClass: 'other',
|
|
688
|
-
};
|
|
689
|
-
}
|
|
690
|
-
if (!lockObserved) {
|
|
691
|
-
// Record the first observation even when the lock is already gone: an
|
|
692
|
-
// absent baseline means the lock changed during the window, which is
|
|
693
|
-
// contention rather than a stuck lock.
|
|
694
|
-
lockObserved = true;
|
|
695
|
-
firstLockMtimeMs = lockMtimeMs(lockPath);
|
|
696
|
-
}
|
|
697
|
-
if (attempt < dependencies.attempts) {
|
|
698
|
-
await dependencies.sleep(dependencies.retryDelaysMs[attempt - 1] ?? 0);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
611
|
+
const artifact = input.identity
|
|
612
|
+
? `${input.logPath}:${input.identity.key}`
|
|
613
|
+
: `${input.logPath}:${createHash('sha256')
|
|
614
|
+
.update(await readFile(input.logPath))
|
|
615
|
+
.digest('hex')}`;
|
|
616
|
+
const result = await commitExactPaths({
|
|
617
|
+
repoRoot: input.repoRoot,
|
|
618
|
+
paths: [
|
|
619
|
+
relative(await realpath(input.repoRoot), await realpath(input.logPath)),
|
|
620
|
+
],
|
|
621
|
+
message,
|
|
622
|
+
identity: `project-log:${artifact}`,
|
|
623
|
+
}, {
|
|
624
|
+
attempts: dependencies.attempts,
|
|
625
|
+
retryDelaysMs: dependencies.retryDelaysMs,
|
|
626
|
+
sleep: dependencies.sleep,
|
|
627
|
+
});
|
|
628
|
+
if (result.outcome === 'committed' ||
|
|
629
|
+
result.outcome === 'already-matching' ||
|
|
630
|
+
result.outcome === 'nothing') {
|
|
631
|
+
const verified = input.identity === undefined
|
|
632
|
+
? 'present'
|
|
633
|
+
: committedInputIdentityState(run, input);
|
|
634
|
+
if (verified !== 'present')
|
|
635
|
+
return {
|
|
636
|
+
outcome: verified === 'absent'
|
|
637
|
+
? 'entry-missing-after-commit'
|
|
638
|
+
: 'commit-unverified',
|
|
639
|
+
committed: false,
|
|
640
|
+
attempts: result.attempts,
|
|
641
|
+
error: verified === 'absent'
|
|
642
|
+
? entryMissingMessage(input)
|
|
643
|
+
: `the committed project log could not be read back to confirm this entry (${input.identity?.key ?? 'unknown'})`,
|
|
644
|
+
};
|
|
645
|
+
return {
|
|
646
|
+
outcome: result.outcome === 'committed'
|
|
647
|
+
? 'committed'
|
|
648
|
+
: result.outcome === 'already-matching' ||
|
|
649
|
+
settledByAnotherWriter(run, snapshot, input)
|
|
650
|
+
? 'already-committed'
|
|
651
|
+
: 'nothing-to-commit',
|
|
652
|
+
committed: result.outcome === 'committed',
|
|
653
|
+
attempts: result.attempts,
|
|
654
|
+
};
|
|
701
655
|
}
|
|
702
|
-
|
|
656
|
+
// Caller-owned entry verification remains authoritative even when another
|
|
657
|
+
// writer finished during the helper's inspected lock window.
|
|
658
|
+
if (!result.committed && settledByAnotherWriter(run, snapshot, input))
|
|
703
659
|
return {
|
|
704
660
|
outcome: 'already-committed',
|
|
705
661
|
committed: false,
|
|
706
|
-
attempts:
|
|
662
|
+
attempts: result.attempts,
|
|
707
663
|
};
|
|
708
|
-
}
|
|
709
664
|
return {
|
|
710
|
-
outcome: 'blocked-by-index-lock',
|
|
665
|
+
outcome: result.outcome === 'blocked' ? 'blocked-by-index-lock' : 'failed',
|
|
711
666
|
committed: false,
|
|
712
|
-
attempts:
|
|
713
|
-
error:
|
|
714
|
-
lockClass:
|
|
667
|
+
attempts: result.attempts,
|
|
668
|
+
error: result.error,
|
|
669
|
+
lockClass: result.lockClass,
|
|
715
670
|
};
|
|
716
671
|
}
|
|
717
672
|
/**
|
|
@@ -720,7 +675,7 @@ async function commitLockedProjectLog(input, dependencies, run, message) {
|
|
|
720
675
|
*
|
|
721
676
|
* The log is tracked, so an uncommitted append leaves the worktree dirty for
|
|
722
677
|
* whatever runs next — including a dispatched subagent whose preflight requires
|
|
723
|
-
* a clean tree. The
|
|
678
|
+
* a clean tree. The shared helper commits exact log paths with enabled hooks so unrelated
|
|
724
679
|
* working-tree changes are never swept in.
|
|
725
680
|
*
|
|
726
681
|
* Scope note: this commits the whole log file, so a log that was already dirty
|
|
@@ -732,8 +687,7 @@ async function commitLockedProjectLog(input, dependencies, run, message) {
|
|
|
732
687
|
* Never throws, and never deletes, moves, or forces an index lock: a lock this
|
|
733
688
|
* process did not take is another process's, and only its owner may clear it.
|
|
734
689
|
* Git failures are reported to the caller, which degrades to a diagnostic
|
|
735
|
-
* rather than altering any exit status.
|
|
736
|
-
* partially staged log is not left behind.
|
|
690
|
+
* rather than altering any exit status. The helper preserves the real index on failure; pending evidence remains resumable.
|
|
737
691
|
*
|
|
738
692
|
* Exactly one outcome is derived from a single pre-action snapshot (entry HEAD
|
|
739
693
|
* plus entry dirtiness) rather than from eligibility re-sampled after the
|
|
@@ -1059,7 +1013,57 @@ export async function appendProjectLog(input, overrides = {}) {
|
|
|
1059
1013
|
const logPath = join(projectPath, PROJECT_LOG_FILENAME);
|
|
1060
1014
|
return withProjectLogLock(logPath, dependencies.lock, 'required', async () => appendLockedProjectLog(input, dependencies, logPath));
|
|
1061
1015
|
}
|
|
1016
|
+
async function previewProjectLogAppend(input, dependencies) {
|
|
1017
|
+
const projectPath = await resolveTargetProject(input, dependencies);
|
|
1018
|
+
const prepared = await prepareProjectLogAppend(input, dependencies, join(projectPath, PROJECT_LOG_FILENAME));
|
|
1019
|
+
if (prepared.status === 'skipped') {
|
|
1020
|
+
return {
|
|
1021
|
+
status: 'dry-run',
|
|
1022
|
+
outcome: 'skipped',
|
|
1023
|
+
reason: prepared.reason,
|
|
1024
|
+
wouldAppend: false,
|
|
1025
|
+
wouldCreate: false,
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
if (prepared.status === 'already-appended') {
|
|
1029
|
+
return {
|
|
1030
|
+
status: 'dry-run',
|
|
1031
|
+
outcome: 'already-appended',
|
|
1032
|
+
logPath: prepared.logPath,
|
|
1033
|
+
heading: prepared.heading,
|
|
1034
|
+
wouldAppend: false,
|
|
1035
|
+
wouldCreate: false,
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
return {
|
|
1039
|
+
status: 'dry-run',
|
|
1040
|
+
outcome: 'append',
|
|
1041
|
+
logPath: prepared.logPath,
|
|
1042
|
+
heading: prepared.heading,
|
|
1043
|
+
wouldAppend: true,
|
|
1044
|
+
wouldCreate: prepared.instantiated !== undefined,
|
|
1045
|
+
...(prepared.normalized ? { normalizedLineEndings: true } : {}),
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1062
1048
|
async function appendLockedProjectLog(input, dependencies, logPath) {
|
|
1049
|
+
// Normal append prepares fresh state only after taking its required lock.
|
|
1050
|
+
const prepared = await prepareProjectLogAppend(input, dependencies, logPath);
|
|
1051
|
+
if (prepared.status !== 'ready')
|
|
1052
|
+
return prepared;
|
|
1053
|
+
if (prepared.instantiated !== undefined) {
|
|
1054
|
+
await dependencies.writeLog(logPath, prepared.instantiated);
|
|
1055
|
+
}
|
|
1056
|
+
await appendEntry(logPath, prepared.heading, prepared.body, prepared.versionNote, dependencies);
|
|
1057
|
+
return {
|
|
1058
|
+
status: 'appended',
|
|
1059
|
+
logPath,
|
|
1060
|
+
heading: prepared.heading,
|
|
1061
|
+
created: prepared.instantiated !== undefined,
|
|
1062
|
+
...(prepared.normalized ? { normalizedLineEndings: true } : {}),
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
/** Read-only validation shared by advisory previews and locked mutations. */
|
|
1066
|
+
async function prepareProjectLogAppend(input, dependencies, logPath) {
|
|
1063
1067
|
const logExists = await fileExists(logPath);
|
|
1064
1068
|
if (!logExists) {
|
|
1065
1069
|
const effective = await dependencies.resolveEffectiveConfig(input.repoRoot, join(input.home ?? homedir(), '.oat'));
|
|
@@ -1070,7 +1074,7 @@ async function appendLockedProjectLog(input, dependencies, logPath) {
|
|
|
1070
1074
|
const date = dependencies.now().toISOString().slice(0, 10);
|
|
1071
1075
|
const { heading, body, versionNote, normalized } = validateEntry(input, date);
|
|
1072
1076
|
const idempotencyKey = validateIdempotencyKey(input.idempotencyKey, body);
|
|
1073
|
-
let
|
|
1077
|
+
let instantiated;
|
|
1074
1078
|
if (logExists) {
|
|
1075
1079
|
const content = await dependencies.readLog(logPath);
|
|
1076
1080
|
// A log whose markers two readers resolve differently cannot answer
|
|
@@ -1157,7 +1161,7 @@ async function appendLockedProjectLog(input, dependencies, logPath) {
|
|
|
1157
1161
|
if (!logExists) {
|
|
1158
1162
|
const assetsRoot = await dependencies.resolveAssetsRoot();
|
|
1159
1163
|
const template = await readFile(join(assetsRoot, 'templates', PROJECT_LOG_FILENAME), 'utf8');
|
|
1160
|
-
|
|
1164
|
+
instantiated = instantiateProjectLogTemplate(template, basename(dirname(logPath)), date);
|
|
1161
1165
|
// The project directory's basename lands in the template's title line, so a
|
|
1162
1166
|
// directory named `demo<U+2028>## Notes` would create a log this command
|
|
1163
1167
|
// then refuses on its next append. A mutator must never write content it
|
|
@@ -1165,16 +1169,15 @@ async function appendLockedProjectLog(input, dependencies, logPath) {
|
|
|
1165
1169
|
if (containsAmbiguousProjectLogMarker(instantiated)) {
|
|
1166
1170
|
throw new Error(`Project name ${basename(dirname(logPath))} would put a '## ' heading after a carriage return, U+2028, or U+2029 in the new log's title, leaving a file with two readings. Rename the project directory using line feeds only.`);
|
|
1167
1171
|
}
|
|
1168
|
-
await dependencies.writeLog(logPath, instantiated);
|
|
1169
|
-
created = true;
|
|
1170
1172
|
}
|
|
1171
|
-
await appendEntry(logPath, heading, body, versionNote, dependencies);
|
|
1172
1173
|
return {
|
|
1173
|
-
status: '
|
|
1174
|
+
status: 'ready',
|
|
1174
1175
|
logPath,
|
|
1175
1176
|
heading,
|
|
1176
|
-
|
|
1177
|
-
|
|
1177
|
+
body,
|
|
1178
|
+
versionNote,
|
|
1179
|
+
normalized,
|
|
1180
|
+
instantiated,
|
|
1178
1181
|
};
|
|
1179
1182
|
}
|
|
1180
1183
|
async function samePath(left, right) {
|
|
@@ -1323,7 +1326,7 @@ async function runAppendCommand(options, context, dependencies) {
|
|
|
1323
1326
|
}
|
|
1324
1327
|
let result;
|
|
1325
1328
|
try {
|
|
1326
|
-
result = await appendProjectLog({
|
|
1329
|
+
result = await (options.dryRun ? previewProjectLogAppend : appendProjectLog)({
|
|
1327
1330
|
repoRoot,
|
|
1328
1331
|
home: context.home,
|
|
1329
1332
|
...options,
|
|
@@ -1352,7 +1355,9 @@ async function runAppendCommand(options, context, dependencies) {
|
|
|
1352
1355
|
}
|
|
1353
1356
|
let commit;
|
|
1354
1357
|
let receiptRemoved = false;
|
|
1355
|
-
if (options.commit &&
|
|
1358
|
+
if (options.commit &&
|
|
1359
|
+
result.status !== 'skipped' &&
|
|
1360
|
+
result.status !== 'dry-run') {
|
|
1356
1361
|
commit = await commitProjectLog({
|
|
1357
1362
|
repoRoot,
|
|
1358
1363
|
logPath: result.logPath,
|
|
@@ -1376,6 +1381,14 @@ async function runAppendCommand(options, context, dependencies) {
|
|
|
1376
1381
|
: {}),
|
|
1377
1382
|
});
|
|
1378
1383
|
}
|
|
1384
|
+
else if (result.status === 'dry-run') {
|
|
1385
|
+
const prediction = result.outcome === 'append'
|
|
1386
|
+
? `Would append project log entry: ${result.logPath}${result.wouldCreate ? ' (would create log)' : ''}.`
|
|
1387
|
+
: result.outcome === 'already-appended'
|
|
1388
|
+
? `Project log entry already present: ${result.heading}.`
|
|
1389
|
+
: 'Would skip project log append (projectLog=false).';
|
|
1390
|
+
context.logger.success(`${prediction} No changes were made.`);
|
|
1391
|
+
}
|
|
1379
1392
|
else if (result.status === 'appended') {
|
|
1380
1393
|
context.logger.success(`Appended project log entry: ${result.logPath}`);
|
|
1381
1394
|
}
|
|
@@ -1406,6 +1419,7 @@ export function createProjectLogAppendCommand(overrides = {}) {
|
|
|
1406
1419
|
const dependencies = { ...DEFAULT_COMMAND_DEPENDENCIES, ...overrides };
|
|
1407
1420
|
return new Command('append')
|
|
1408
1421
|
.description('Append a validated entry to the active project log')
|
|
1422
|
+
.option('--dry-run', 'Validate and preview without writing files, acquiring locks, or committing')
|
|
1409
1423
|
.option('--structural', 'Write a structural lifecycle entry')
|
|
1410
1424
|
.option('--type <type>', 'Judgment type: bug, friction, worked-well, feedback')
|
|
1411
1425
|
.option('--scope <scope>', 'Judgment scope: project or general')
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { type CommandContext, type GlobalOptions } from '../../../app/command-context.js';
|
|
2
2
|
import { type GitRunner } from '../../project/sync/git.js';
|
|
3
|
-
import {
|
|
3
|
+
import { type MigrateResult, type SyncTarget } from '../../project/sync/ref-sync.js';
|
|
4
4
|
import { resolveProjectsRoot } from '../../shared/oat-paths.js';
|
|
5
5
|
import { Command } from 'commander';
|
|
6
6
|
interface ProjectMigrateDependencies {
|
|
7
7
|
buildCommandContext: (options: GlobalOptions) => CommandContext;
|
|
8
8
|
resolveProjectRoot: (cwd: string) => Promise<string>;
|
|
9
9
|
resolveProjectsRoot: typeof resolveProjectsRoot;
|
|
10
|
-
assertConfinedMigrationSource: typeof assertConfinedMigrationSource;
|
|
11
10
|
migrateSharedToSynced: (target: SyncTarget, git: GitRunner, options: {
|
|
12
11
|
sourcePath: string;
|
|
13
12
|
commit: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/migrate/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/migrate/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAUjE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,UAAU,0BAA0B;IAClC,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,qBAAqB,EAAE,CACrB,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,KACxD,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5B,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAoFD,wBAAgB,2BAA2B,CACzC,SAAS,GAAE,OAAO,CAAC,0BAA0B,CAAM,GAClD,OAAO,CAmBT"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { lstat } from 'node:fs/promises';
|
|
1
2
|
import { basename, dirname, isAbsolute, resolve } from 'node:path';
|
|
2
3
|
import { buildCommandContext, } from '../../../app/command-context.js';
|
|
3
4
|
import { defaultGitRunner } from '../../project/sync/git.js';
|
|
@@ -12,7 +13,6 @@ const DEFAULT_DEPENDENCIES = {
|
|
|
12
13
|
buildCommandContext,
|
|
13
14
|
resolveProjectRoot,
|
|
14
15
|
resolveProjectsRoot,
|
|
15
|
-
assertConfinedMigrationSource,
|
|
16
16
|
migrateSharedToSynced,
|
|
17
17
|
gitRunner: defaultGitRunner,
|
|
18
18
|
processEnv: process.env,
|
|
@@ -35,7 +35,13 @@ async function runMigrate(context, projectPath, options, dependencies) {
|
|
|
35
35
|
const slug = basename(sourcePath);
|
|
36
36
|
assertValidProjectSlug(slug, 1);
|
|
37
37
|
const target = buildSyncTarget(repoRoot, projectsRoot, slug);
|
|
38
|
-
await
|
|
38
|
+
const source = await lstat(sourcePath).catch((error) => {
|
|
39
|
+
if (error.code === 'ENOENT')
|
|
40
|
+
return undefined;
|
|
41
|
+
throw error;
|
|
42
|
+
});
|
|
43
|
+
if (source)
|
|
44
|
+
await assertConfinedMigrationSource(target, sourcePath);
|
|
39
45
|
if (resolveProjectScope(sourcePath, sharedRoot, repoRoot) !== 'shared') {
|
|
40
46
|
throw new CliError(`Project ${projectPath} must be a direct child in shared scope.`, 1);
|
|
41
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/new/scaffold.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/new/scaffold.ts"],"names":[],"mappings":"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"}
|
|
@@ -499,10 +499,10 @@ export async function scaffoldProject(options, overrides = {}) {
|
|
|
499
499
|
if (!recordWritten) {
|
|
500
500
|
throw new CliError(`Synced project ${options.projectName} was published, but its discovery record was not written: ${detail}. Run oat project pull '${options.projectName}' to resume record adoption; do not rerun project creation.`, 2);
|
|
501
501
|
}
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
.
|
|
505
|
-
throw new CliError(`Synced project ${options.projectName} and its discovery record were written, but the parent commit failed: ${detail}.
|
|
502
|
+
const recoveryGuidance = detail.includes('oat internal commit-paths --identity ')
|
|
503
|
+
? 'Use the exact identity and paths in the reported recovery command'
|
|
504
|
+
: 'Preserve the existing checkout, remote ref, discovery record, and any commit receipt. Inspect the parent commit and repair the reported condition before retrying record persistence';
|
|
505
|
+
throw new CliError(`Synced project ${options.projectName} and its discovery record were written, but the parent commit failed: ${detail}. ${recoveryGuidance}; do not rerun project creation.`, 2);
|
|
506
506
|
}
|
|
507
507
|
throw error;
|
|
508
508
|
}
|
|
@@ -2,6 +2,7 @@ import { mkdir as defaultMkdir, readFile as defaultReadFile, rename as defaultRe
|
|
|
2
2
|
import { type CommandContext, type GlobalOptions } from '../../../app/command-context.js';
|
|
3
3
|
import { type GitRunner } from '../../project/sync/git.js';
|
|
4
4
|
import { type PushResult, type SyncTarget } from '../../project/sync/ref-sync.js';
|
|
5
|
+
import { type ExactPathCommitResult } from '../../shared/exact-path-commit.js';
|
|
5
6
|
import { resolveTemplate as defaultResolveTemplate } from '../../shared/template-source.js';
|
|
6
7
|
import { fileExists as defaultFileExists } from '../../../fs/io.js';
|
|
7
8
|
import { Command } from 'commander';
|
|
@@ -15,6 +16,12 @@ export interface LitePlanSections {
|
|
|
15
16
|
technicalDesign?: string;
|
|
16
17
|
}
|
|
17
18
|
type PromotionReason = 'promoted' | 'unsupported-target' | 'not-lite' | 'lite-plan-reference-exists' | 'invalid-lite-plan' | 'scope-unresolved' | 'project-unreadable' | 'template-unreadable' | 'write-failed' | 'persistence-failed';
|
|
19
|
+
interface PromotionRecovery extends ExactPathCommitResult {
|
|
20
|
+
identity: string;
|
|
21
|
+
message: string;
|
|
22
|
+
paths: string[];
|
|
23
|
+
command: string;
|
|
24
|
+
}
|
|
18
25
|
export type PromotionResult = {
|
|
19
26
|
status: 'promoted';
|
|
20
27
|
reason: 'promoted';
|
|
@@ -23,6 +30,7 @@ export type PromotionResult = {
|
|
|
23
30
|
status: 'refused';
|
|
24
31
|
reason: Exclude<PromotionReason, 'promoted'>;
|
|
25
32
|
files: string[];
|
|
33
|
+
recovery?: PromotionRecovery;
|
|
26
34
|
};
|
|
27
35
|
export interface ProjectPromoteDependencies {
|
|
28
36
|
buildCommandContext: (options: GlobalOptions) => CommandContext;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promote.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/promote/promote.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"promote.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/promote/promote.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import { mkdir as defaultMkdir, readFile as defaultReadFile, rename as defaultRename, writeFile as defaultWriteFile, } from 'node:fs/promises';
|
|
2
3
|
import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
3
4
|
import { buildCommandContext, } from '../../../app/command-context.js';
|
|
4
5
|
import { applyTemplateReplacements } from '../../project/new/scaffold.js';
|
|
5
6
|
import { defaultGitRunner } from '../../project/sync/git.js';
|
|
6
7
|
import { buildSyncTarget, pushSynced as defaultPushSynced, } from '../../project/sync/ref-sync.js';
|
|
8
|
+
import { commitExactPaths, } from '../../shared/exact-path-commit.js';
|
|
7
9
|
import { getFrontmatterBlock } from '../../shared/frontmatter.js';
|
|
8
10
|
import { replaceFrontmatter } from '../../shared/frontmatter-write.js';
|
|
9
11
|
import { resolveProjectsRoot as defaultResolveProjectsRoot } from '../../shared/oat-paths.js';
|
|
@@ -28,6 +30,16 @@ const LITE_SECTION_HEADINGS = [
|
|
|
28
30
|
['Out of Scope', 'outOfScope'],
|
|
29
31
|
['Validation Criteria', 'validationCriteria'],
|
|
30
32
|
];
|
|
33
|
+
class PromotionPersistenceError extends Error {
|
|
34
|
+
recovery;
|
|
35
|
+
constructor(recovery) {
|
|
36
|
+
super(`Exact-path promotion commit ${recovery.outcome}: ${recovery.error ?? 'unverified result'}`);
|
|
37
|
+
this.recovery = recovery;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function shellQuote(value) {
|
|
41
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
42
|
+
}
|
|
31
43
|
const DEFAULT_DEPENDENCIES = {
|
|
32
44
|
buildCommandContext,
|
|
33
45
|
resolveProjectRoot,
|
|
@@ -169,7 +181,7 @@ function renderQuickState(stateContent, statePath, nowUtc) {
|
|
|
169
181
|
function repoRelativePath(repoRoot, path) {
|
|
170
182
|
return relative(repoRoot, path).split(sep).join('/');
|
|
171
183
|
}
|
|
172
|
-
async function persistPromotion(repoRoot, projectsRoot, projectRoot, projectName, scope, dependencies) {
|
|
184
|
+
async function persistPromotion(repoRoot, projectsRoot, projectRoot, projectName, scope, identity, dependencies) {
|
|
173
185
|
const message = `chore(oat): promote ${projectName} to quick`;
|
|
174
186
|
if (scope === 'synced') {
|
|
175
187
|
const target = buildSyncTarget(repoRoot, projectsRoot, projectName);
|
|
@@ -183,10 +195,23 @@ async function persistPromotion(repoRoot, projectsRoot, projectRoot, projectName
|
|
|
183
195
|
return;
|
|
184
196
|
}
|
|
185
197
|
const pathspecs = PROMOTED_FILES.map((file) => repoRelativePath(repoRoot, join(projectRoot, file)));
|
|
186
|
-
await dependencies.gitRunner.run(['
|
|
198
|
+
await dependencies.gitRunner.run(['rev-parse', '--is-inside-work-tree'], {
|
|
187
199
|
cwd: repoRoot,
|
|
188
200
|
});
|
|
189
|
-
|
|
201
|
+
const result = await commitExactPaths({
|
|
202
|
+
repoRoot,
|
|
203
|
+
paths: pathspecs,
|
|
204
|
+
message,
|
|
205
|
+
identity,
|
|
206
|
+
});
|
|
207
|
+
if (!['committed', 'already-matching', 'nothing'].includes(result.outcome))
|
|
208
|
+
throw new PromotionPersistenceError({
|
|
209
|
+
...result,
|
|
210
|
+
identity,
|
|
211
|
+
message,
|
|
212
|
+
paths: pathspecs,
|
|
213
|
+
command: `oat --cwd ${shellQuote(repoRoot)} internal commit-paths --identity ${shellQuote(identity)} --message ${shellQuote(message)} -- ${pathspecs.map(shellQuote).join(' ')}`,
|
|
214
|
+
});
|
|
190
215
|
}
|
|
191
216
|
async function promoteProject(projectPath, options, context, dependencies) {
|
|
192
217
|
if (options.to !== 'quick') {
|
|
@@ -268,6 +293,9 @@ async function promoteProject(projectPath, options, context, dependencies) {
|
|
|
268
293
|
catch {
|
|
269
294
|
return { status: 'refused', reason: 'template-unreadable', files: [] };
|
|
270
295
|
}
|
|
296
|
+
// Bind this generation before writes, and retain the exact identity in any
|
|
297
|
+
// persistence recovery rather than deriving it again from the reusable slug.
|
|
298
|
+
const identity = `promote:${projectRoot}:lite-to-quick:${randomUUID()}`;
|
|
271
299
|
const writtenFiles = [];
|
|
272
300
|
try {
|
|
273
301
|
await dependencies.mkdir(join(projectRoot, 'references'), {
|
|
@@ -286,13 +314,16 @@ async function promoteProject(projectPath, options, context, dependencies) {
|
|
|
286
314
|
return { status: 'refused', reason: 'write-failed', files: writtenFiles };
|
|
287
315
|
}
|
|
288
316
|
try {
|
|
289
|
-
await persistPromotion(repoRoot, projectsRoot, projectRoot, projectName, scope, dependencies);
|
|
317
|
+
await persistPromotion(repoRoot, projectsRoot, projectRoot, projectName, scope, identity, dependencies);
|
|
290
318
|
}
|
|
291
|
-
catch {
|
|
319
|
+
catch (error) {
|
|
292
320
|
return {
|
|
293
321
|
status: 'refused',
|
|
294
322
|
reason: 'persistence-failed',
|
|
295
323
|
files: writtenFiles,
|
|
324
|
+
...(error instanceof PromotionPersistenceError
|
|
325
|
+
? { recovery: error.recovery }
|
|
326
|
+
: {}),
|
|
296
327
|
};
|
|
297
328
|
}
|
|
298
329
|
return {
|
|
@@ -317,7 +348,7 @@ export function createProjectPromoteCommand(overrides = {}) {
|
|
|
317
348
|
context.logger.info(`Promoted ${projectPath} to quick.`);
|
|
318
349
|
}
|
|
319
350
|
else {
|
|
320
|
-
context.logger.error(`Promotion refused: ${result.reason}
|
|
351
|
+
context.logger.error(`Promotion refused: ${result.reason}.${result.recovery ? ` Exact-path persistence ${result.recovery.outcome}; attempts ${result.recovery.attempts}; lock ${result.recovery.lockClass ?? 'none'}; identity ${result.recovery.identity}; receipt ${result.recovery.receipt ?? 'unavailable'}. ${result.recovery.error ?? ''} Recover existing Quick artifacts with: ${result.recovery.command}` : ''}`);
|
|
321
352
|
}
|
|
322
353
|
process.exitCode = result.status === 'promoted' ? 0 : 1;
|
|
323
354
|
});
|