@deftai/directive-core 0.104.0 → 0.106.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/dist/authz/classify.js +1108 -67
- package/dist/check/cached-orchestrator.d.ts +1 -1
- package/dist/check/cached-orchestrator.js +2 -2
- package/dist/check/named-cause.d.ts +1 -1
- package/dist/check/named-cause.js +34 -4
- package/dist/codebase/default-extractor.d.ts +5 -0
- package/dist/codebase/default-extractor.js +7 -15
- package/dist/deposit/copy-tree.js +60 -0
- package/dist/deposit/python-free.js +26 -18
- package/dist/doctor/checks.d.ts +8 -0
- package/dist/doctor/checks.js +37 -25
- package/dist/doctor/doctor-state.d.ts +4 -0
- package/dist/doctor/doctor-state.js +7 -1
- package/dist/doctor/main.js +9 -19
- package/dist/doctor/types.d.ts +9 -0
- package/dist/forward-coverage/diff-coverage.d.ts +39 -0
- package/dist/forward-coverage/diff-coverage.js +112 -0
- package/dist/forward-coverage/diff-lines.d.ts +19 -0
- package/dist/forward-coverage/diff-lines.js +173 -0
- package/dist/forward-coverage/evaluate.d.ts +24 -9
- package/dist/forward-coverage/evaluate.js +125 -19
- package/dist/fs/contained-write.d.ts +115 -1
- package/dist/fs/contained-write.js +241 -5
- package/dist/fs/mutation-ledger.d.ts +54 -0
- package/dist/fs/mutation-ledger.js +133 -0
- package/dist/fs/non-product-dirs.d.ts +79 -0
- package/dist/fs/non-product-dirs.js +96 -0
- package/dist/hooks/dispatcher.d.ts +3 -1
- package/dist/hooks/dispatcher.js +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/init-deposit/agent-hooks.d.ts +1 -1
- package/dist/init-deposit/agent-hooks.js +18 -16
- package/dist/init-deposit/core-guard-pin-content.d.ts +7 -0
- package/dist/init-deposit/core-guard-pin-content.js +393 -0
- package/dist/init-deposit/gitignore.js +9 -0
- package/dist/init-deposit/hygiene.d.ts +47 -11
- package/dist/init-deposit/hygiene.js +172 -37
- package/dist/init-deposit/legacy-detect.js +6 -2
- package/dist/init-deposit/refresh.d.ts +21 -1
- package/dist/init-deposit/refresh.js +106 -9
- package/dist/init-deposit/scaffold.js +28 -279
- package/dist/init-deposit/slash-deposit.js +13 -7
- package/dist/init-deposit/xbrief-projections.d.ts +8 -1
- package/dist/init-deposit/xbrief-projections.js +24 -11
- package/dist/intake/clause-derivation.d.ts +47 -1
- package/dist/intake/clause-derivation.js +238 -9
- package/dist/intake/issue-ingest.d.ts +3 -1
- package/dist/intake/issue-ingest.js +35 -6
- package/dist/lifecycle/completed-tracked-on-delivery.d.ts +5 -0
- package/dist/lifecycle/completed-tracked-on-delivery.js +85 -18
- package/dist/lifecycle-visible/evaluate.d.ts +98 -0
- package/dist/lifecycle-visible/evaluate.js +710 -0
- package/dist/lifecycle-visible/index.d.ts +2 -0
- package/dist/lifecycle-visible/index.js +2 -0
- package/dist/literal-acceptance/capture.d.ts +10 -0
- package/dist/literal-acceptance/capture.js +226 -77
- package/dist/literal-acceptance/evaluate.d.ts +29 -4
- package/dist/literal-acceptance/evaluate.js +70 -2
- package/dist/literal-acceptance/index.d.ts +3 -3
- package/dist/literal-acceptance/index.js +3 -3
- package/dist/literal-acceptance/safety.d.ts +41 -0
- package/dist/literal-acceptance/safety.js +126 -2
- package/dist/literal-acceptance/types.d.ts +8 -0
- package/dist/orchestration/judgment-policy.d.ts +13 -0
- package/dist/orchestration/judgment-policy.js +15 -0
- package/dist/orchestration/probe-session.js +13 -8
- package/dist/orchestration/verify-judgment-gates.d.ts +4 -0
- package/dist/orchestration/verify-judgment-gates.js +43 -11
- package/dist/orphan-active/evaluate.d.ts +7 -1
- package/dist/orphan-active/evaluate.js +102 -23
- package/dist/platform/platform-capabilities.js +2 -3
- package/dist/policy/base-branch.d.ts +37 -0
- package/dist/policy/base-branch.js +114 -0
- package/dist/policy/branch-sync.d.ts +80 -0
- package/dist/policy/branch-sync.js +211 -0
- package/dist/policy/ceremony-dial-escalation.js +1 -0
- package/dist/policy/ceremony-dial.js +11 -21
- package/dist/policy/delivery-branch.d.ts +5 -3
- package/dist/policy/delivery-branch.js +7 -3
- package/dist/policy/file-size-thresholds.d.ts +24 -0
- package/dist/policy/file-size-thresholds.js +23 -0
- package/dist/policy/forge-outage-retry.d.ts +47 -0
- package/dist/policy/forge-outage-retry.js +157 -0
- package/dist/policy/host-hooks.d.ts +20 -0
- package/dist/policy/host-hooks.js +121 -2
- package/dist/policy/index.d.ts +7 -0
- package/dist/policy/index.js +66 -7
- package/dist/policy/org-force-on-migration.js +2 -1
- package/dist/policy/plan-extensions.d.ts +1 -1
- package/dist/policy/plan-extensions.js +2 -0
- package/dist/policy/product-signal.js +6 -7
- package/dist/policy/project-invariants.d.ts +50 -0
- package/dist/policy/project-invariants.js +205 -0
- package/dist/policy/require-human-merge.js +8 -6
- package/dist/policy/resolve.d.ts +7 -2
- package/dist/policy/resolve.js +25 -8
- package/dist/policy/sync-default.d.ts +129 -0
- package/dist/policy/sync-default.js +452 -0
- package/dist/policy/sync-max-files.d.ts +93 -0
- package/dist/policy/sync-max-files.js +192 -0
- package/dist/policy/value-feedback.js +7 -8
- package/dist/pr-monitor/absent-required.d.ts +11 -0
- package/dist/pr-monitor/absent-required.js +61 -0
- package/dist/pr-monitor/constants.d.ts +7 -0
- package/dist/pr-monitor/constants.js +7 -0
- package/dist/pr-monitor/index.d.ts +2 -1
- package/dist/pr-monitor/index.js +2 -1
- package/dist/pr-monitor/main.js +4 -0
- package/dist/pr-monitor/monitor.js +24 -1
- package/dist/pr-wait-mergeable/classify.js +6 -0
- package/dist/preflight/evaluate.d.ts +4 -0
- package/dist/preflight/evaluate.js +35 -1
- package/dist/preflight/index.d.ts +4 -0
- package/dist/preflight/index.js +2 -0
- package/dist/preflight/intended-placement.d.ts +62 -0
- package/dist/preflight/intended-placement.js +275 -0
- package/dist/preflight/project-invariants-gate.d.ts +29 -0
- package/dist/preflight/project-invariants-gate.js +82 -0
- package/dist/preflight-cache/evaluate.d.ts +12 -0
- package/dist/preflight-cache/evaluate.js +15 -1
- package/dist/preflight-cache/index.d.ts +1 -1
- package/dist/preflight-cache/index.js +1 -1
- package/dist/product-first-done-gate/acceptance-resolver.d.ts +128 -0
- package/dist/product-first-done-gate/acceptance-resolver.js +144 -0
- package/dist/product-first-done-gate/acceptance.d.ts +7 -4
- package/dist/product-first-done-gate/acceptance.js +79 -8
- package/dist/product-first-done-gate/empty-resolution.d.ts +4 -1
- package/dist/product-first-done-gate/empty-resolution.js +12 -1
- package/dist/product-first-done-gate/evaluate.d.ts +16 -0
- package/dist/product-first-done-gate/evaluate.js +298 -34
- package/dist/product-first-done-gate/index.d.ts +2 -0
- package/dist/product-first-done-gate/index.js +1 -0
- package/dist/release/build-dist.d.ts +101 -0
- package/dist/release/build-dist.js +428 -46
- package/dist/release/closed-verb-gate.d.ts +12 -0
- package/dist/release/closed-verb-gate.js +51 -0
- package/dist/release/index.d.ts +1 -0
- package/dist/release/index.js +1 -0
- package/dist/release/pipeline.js +7 -0
- package/dist/release/types.d.ts +12 -0
- package/dist/render/constants.d.ts +2 -1
- package/dist/render/constants.js +2 -1
- package/dist/render/framework-commands.js +4 -0
- package/dist/render/spec-validate.js +23 -12
- package/dist/run-summary/emit.d.ts +16 -10
- package/dist/run-summary/emit.js +26 -15
- package/dist/run-summary/index.d.ts +3 -2
- package/dist/run-summary/index.js +2 -1
- package/dist/run-summary/session-id.d.ts +21 -0
- package/dist/run-summary/session-id.js +92 -0
- package/dist/run-summary/types.d.ts +33 -4
- package/dist/run-summary/types.js +1 -0
- package/dist/scope/acceptance-evidence.d.ts +19 -7
- package/dist/scope/acceptance-evidence.js +38 -11
- package/dist/scope/delivery-evidence.d.ts +3 -3
- package/dist/scope/delivery-evidence.js +22 -27
- package/dist/scope/index.d.ts +1 -0
- package/dist/scope/index.js +1 -0
- package/dist/scope/main.js +15 -5
- package/dist/scope/parent-lineage.js +7 -6
- package/dist/scope/project-invariant-coverage.d.ts +54 -0
- package/dist/scope/project-invariant-coverage.js +322 -0
- package/dist/scope/promote-from-issue.js +5 -5
- package/dist/scope/transition.js +1 -1
- package/dist/scope-provenance/compare-intent.d.ts +27 -0
- package/dist/scope-provenance/compare-intent.js +233 -0
- package/dist/scope-provenance/digest.d.ts +18 -1
- package/dist/scope-provenance/digest.js +16 -11
- package/dist/scope-provenance/evaluate.d.ts +5 -1
- package/dist/scope-provenance/evaluate.js +67 -7
- package/dist/scope-provenance/extract-intent.d.ts +43 -0
- package/dist/scope-provenance/extract-intent.js +262 -0
- package/dist/scope-provenance/index.d.ts +7 -1
- package/dist/scope-provenance/index.js +7 -1
- package/dist/scope-provenance/intent-digest.d.ts +12 -0
- package/dist/scope-provenance/intent-digest.js +33 -0
- package/dist/scope-provenance/intent-evaluate.d.ts +43 -0
- package/dist/scope-provenance/intent-evaluate.js +149 -0
- package/dist/scope-provenance/json-tokenizer.d.ts +19 -0
- package/dist/scope-provenance/json-tokenizer.js +225 -0
- package/dist/scope-provenance/known-machine.d.ts +38 -0
- package/dist/scope-provenance/known-machine.js +157 -0
- package/dist/scope-provenance/mint-artifacts.d.ts +102 -0
- package/dist/scope-provenance/mint-artifacts.js +425 -0
- package/dist/session/ac-pass-banking.d.ts +9 -36
- package/dist/session/ac-pass-banking.js +33 -79
- package/dist/session/ac-pass-reuse.d.ts +53 -0
- package/dist/session/ac-pass-reuse.js +95 -0
- package/dist/session/git.js +19 -4
- package/dist/session/index.d.ts +4 -0
- package/dist/session/index.js +4 -0
- package/dist/session/occupancy-stress-worker.d.ts +7 -0
- package/dist/session/occupancy-stress-worker.js +36 -0
- package/dist/session/occupancy.d.ts +97 -0
- package/dist/session/occupancy.js +422 -0
- package/dist/session/openclaw-soft-rebind-deposit.js +6 -6
- package/dist/session/process-cost.d.ts +32 -10
- package/dist/session/process-cost.js +189 -1
- package/dist/session/product-state-hash.d.ts +26 -0
- package/dist/session/product-state-hash.js +700 -0
- package/dist/session/ritual-entrypoint.js +16 -1
- package/dist/session/session-ready.d.ts +2 -0
- package/dist/session/session-ready.js +52 -30
- package/dist/session/session-start.d.ts +13 -0
- package/dist/session/session-start.js +103 -9
- package/dist/session/verify-ac-session-cache.d.ts +42 -0
- package/dist/session/verify-ac-session-cache.js +126 -0
- package/dist/session/verify-session-ritual.d.ts +28 -0
- package/dist/session/verify-session-ritual.js +97 -5
- package/dist/slash/openclaw-deposit.js +13 -9
- package/dist/slice/constants.d.ts +2 -2
- package/dist/slice/constants.js +2 -2
- package/dist/slice/existing.js +9 -3
- package/dist/slice/lock.d.ts +16 -1
- package/dist/slice/lock.js +137 -11
- package/dist/story-ready/evaluate.d.ts +2 -0
- package/dist/story-ready/evaluate.js +13 -0
- package/dist/swarm/complete-cohort.d.ts +3 -0
- package/dist/swarm/complete-cohort.js +59 -11
- package/dist/swarm/finalize-cohort-cli.d.ts +7 -2
- package/dist/swarm/finalize-cohort-cli.js +165 -35
- package/dist/swarm/finalize-cohort.d.ts +1 -0
- package/dist/swarm/finalize-cohort.js +47 -4
- package/dist/swarm/launch.d.ts +22 -0
- package/dist/swarm/launch.js +155 -0
- package/dist/swarm/routing-set-cli.d.ts +11 -1
- package/dist/swarm/routing-set-cli.js +15 -3
- package/dist/swarm/routing-verify.d.ts +13 -1
- package/dist/swarm/routing-verify.js +60 -21
- package/dist/swarm/routing.d.ts +14 -0
- package/dist/swarm/routing.js +53 -3
- package/dist/telemetry-coverage/fake-trial.js +10 -0
- package/dist/telemetry-coverage/kinds.js +2 -0
- package/dist/triage/actions/index.js +4 -1
- package/dist/triage/bootstrap/index.js +6 -6
- package/dist/triage/help/registry-data.d.ts +1 -1
- package/dist/triage/help/registry-data.js +1 -1
- package/dist/triage/welcome/writers.d.ts +1 -1
- package/dist/triage/welcome/writers.js +10 -5
- package/dist/ts-check-lane/index.d.ts +3 -0
- package/dist/ts-check-lane/index.js +2 -0
- package/dist/ts-check-lane/progress-reporter.d.ts +17 -0
- package/dist/ts-check-lane/progress-reporter.js +38 -0
- package/dist/ts-check-lane/progress.d.ts +32 -0
- package/dist/ts-check-lane/progress.js +57 -0
- package/dist/ts-check-lane/run-lane.d.ts +3 -1
- package/dist/ts-check-lane/run-lane.js +8 -6
- package/dist/validate-content/validate-links.d.ts +8 -0
- package/dist/validate-content/validate-links.js +9 -15
- package/dist/value/feedback-file.js +12 -2
- package/dist/value/readback.d.ts +4 -1
- package/dist/value/readback.js +51 -12
- package/dist/vbrief-build/index.d.ts +1 -0
- package/dist/vbrief-build/index.js +1 -0
- package/dist/vbrief-build/project-invariants-io.d.ts +16 -0
- package/dist/vbrief-build/project-invariants-io.js +41 -0
- package/dist/vbrief-reconcile/graph.js +18 -1
- package/dist/vbrief-reconcile/index.d.ts +1 -1
- package/dist/vbrief-reconcile/index.js +1 -1
- package/dist/vbrief-reconcile/types.d.ts +13 -0
- package/dist/vbrief-reconcile/umbrellas.d.ts +14 -0
- package/dist/vbrief-reconcile/umbrellas.js +261 -26
- package/dist/vbrief-validate/index.d.ts +1 -1
- package/dist/vbrief-validate/index.js +1 -1
- package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
- package/dist/vbrief-validate/plan-hooks.js +68 -0
- package/dist/vbrief-validate/schema.d.ts +4 -0
- package/dist/vbrief-validate/schema.js +1 -1
- package/dist/verify-ac/evaluate.d.ts +7 -1
- package/dist/verify-ac/evaluate.js +34 -26
- package/dist/verify-ac/flag.d.ts +4 -0
- package/dist/verify-ac/flag.js +16 -0
- package/dist/verify-ac/index.d.ts +2 -2
- package/dist/verify-ac/index.js +2 -2
- package/dist/verify-env/agent-hook-readiness.d.ts +2 -2
- package/dist/verify-env/agent-hook-readiness.js +12 -8
- package/dist/verify-env/agent-hooks-live-probe.d.ts +5 -1
- package/dist/verify-env/agent-hooks-live-probe.js +26 -5
- package/dist/verify-env/agent-hooks.js +3 -4
- package/dist/verify-source/contained-writes.js +2 -0
- package/dist/verify-source/verify-stubs.d.ts +15 -1
- package/dist/verify-source/verify-stubs.js +20 -16
- package/dist/vitest-runner/win32-coverage-tmp-setup.d.ts +5 -6
- package/dist/vitest-runner/win32-coverage-tmp-setup.js +5 -6
- package/dist/xbrief-migrate/migrate-project.js +2 -5
- package/package.json +7 -3
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
* Directive thin wrappers (`isThinWrapperMarkdown`). Consumer-customized content
|
|
14
14
|
* at a product filename is left untouched.
|
|
15
15
|
*/
|
|
16
|
-
import { existsSync,
|
|
16
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
17
17
|
import { basename, dirname, join, resolve } from "node:path";
|
|
18
18
|
import { assertDepositContained } from "../deposit/contain.js";
|
|
19
|
-
import { containedWrite } from "../fs/contained-write.js";
|
|
19
|
+
import { containedMkdir, containedRemove, containedRename, containedWrite, } from "../fs/contained-write.js";
|
|
20
20
|
import { isHostSlashCommandDepositEnabled, loadHostSlashCommandsPolicyFromProject, } from "../policy/host-slash-commands.js";
|
|
21
21
|
import { emitHostCommandFiles, getHostCommandLayout, isThinWrapperMarkdown, listSlashEmitterHosts, } from "../slash/index.js";
|
|
22
22
|
import { listProductCommands, logicalIdToFilename } from "../slash/product-set.js";
|
|
@@ -115,7 +115,7 @@ function writeHostCommandFileIfChanged(projectRoot, file) {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
const parent = dirname(absolute);
|
|
118
|
-
|
|
118
|
+
containedMkdir({ root: projectRoot, target: parent });
|
|
119
119
|
// Atomic replace via temp under project root (#2951).
|
|
120
120
|
const tmpName = `${basename(absolute)}.deft-${process.pid}.tmp`;
|
|
121
121
|
const temporary = join(parent, tmpName);
|
|
@@ -126,11 +126,16 @@ function writeHostCommandFileIfChanged(projectRoot, file) {
|
|
|
126
126
|
data: file.contents,
|
|
127
127
|
mode: "replace",
|
|
128
128
|
});
|
|
129
|
-
|
|
129
|
+
containedRename({
|
|
130
|
+
root: projectRoot,
|
|
131
|
+
from: temporary,
|
|
132
|
+
to: absolute,
|
|
133
|
+
mutation: false,
|
|
134
|
+
});
|
|
130
135
|
}
|
|
131
136
|
catch (err) {
|
|
132
137
|
try {
|
|
133
|
-
|
|
138
|
+
containedRemove({ root: projectRoot, target: temporary, mutation: false });
|
|
134
139
|
}
|
|
135
140
|
catch {
|
|
136
141
|
/* best-effort cleanup */
|
|
@@ -162,8 +167,9 @@ function stripManagedHostCommandFiles(projectRoot, hostId) {
|
|
|
162
167
|
continue;
|
|
163
168
|
}
|
|
164
169
|
try {
|
|
165
|
-
|
|
166
|
-
|
|
170
|
+
if (containedRemove({ root: projectRoot, target: absolute }).removed) {
|
|
171
|
+
removed.push(file.relativePath);
|
|
172
|
+
}
|
|
167
173
|
}
|
|
168
174
|
catch {
|
|
169
175
|
/* best-effort */
|
|
@@ -7,11 +7,18 @@
|
|
|
7
7
|
*/
|
|
8
8
|
/** Rewrite legacy vbrief/.eval description paths for xbrief/schemas projection (#2670). */
|
|
9
9
|
export declare function rewriteProjectedSchemaContent(content: string): string;
|
|
10
|
+
/** Fail closed when planned (source-rewritten) schema bytes still cite vbrief/.eval/. */
|
|
11
|
+
export declare function assertPlannedSchemaDescriptionRooted(rel: string, text: string): void;
|
|
10
12
|
/**
|
|
11
13
|
* Fail closed when a projected xbrief/schemas file still cites vbrief/.eval/.
|
|
12
14
|
* Upstream vbrief/schemas/ may keep legacy paths; consumer copies must not (#2670).
|
|
15
|
+
*
|
|
16
|
+
* `skipRel` is dest files the collect record plans to replace (#3456 plan-note):
|
|
17
|
+
* never validate stale dest bytes a skipped write would have replaced.
|
|
13
18
|
*/
|
|
14
|
-
export declare function assertProjectedSchemaDescriptionsRooted(projectDir: string, destinationDir: string
|
|
19
|
+
export declare function assertProjectedSchemaDescriptionsRooted(projectDir: string, destinationDir: string, options?: {
|
|
20
|
+
readonly skipRel?: ReadonlySet<string>;
|
|
21
|
+
}): void;
|
|
15
22
|
/**
|
|
16
23
|
* Synchronize framework-owned xBRIEF schemas while preserving unknown consumer
|
|
17
24
|
* files. The obsolete v0.6 root schema is the only destination-only file this
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* freshness. A current `.deft/core/VERSION` therefore cannot short-circuit
|
|
6
6
|
* these repairs.
|
|
7
7
|
*/
|
|
8
|
-
import { existsSync,
|
|
8
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
9
9
|
import { join, relative, resolve } from "node:path";
|
|
10
|
-
import { containedWrite } from "../fs/contained-write.js";
|
|
10
|
+
import { containedMkdir, containedRemove, containedWrite } from "../fs/contained-write.js";
|
|
11
11
|
import { assertDestinationNotSymlink } from "../fs/projection-containment.js";
|
|
12
12
|
import { resolveLifecycleRoot } from "../layout/resolve.js";
|
|
13
13
|
import { DEV_FALLBACK } from "../platform/constants.js";
|
|
@@ -50,20 +50,30 @@ export function rewriteProjectedSchemaContent(content) {
|
|
|
50
50
|
? content.replaceAll(LEGACY_EVAL_PATH_PREFIX, XBRIEF_EVAL_PATH_PREFIX)
|
|
51
51
|
: content;
|
|
52
52
|
}
|
|
53
|
+
/** Fail closed when planned (source-rewritten) schema bytes still cite vbrief/.eval/. */
|
|
54
|
+
export function assertPlannedSchemaDescriptionRooted(rel, text) {
|
|
55
|
+
if (text.includes(LEGACY_EVAL_PATH_PREFIX)) {
|
|
56
|
+
throw new Error(`projected xbrief schema still cites ${LEGACY_EVAL_PATH_PREFIX}: ${join(MIGRATED_ARTIFACT_DIR, "schemas", rel)}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
53
59
|
/**
|
|
54
60
|
* Fail closed when a projected xbrief/schemas file still cites vbrief/.eval/.
|
|
55
61
|
* Upstream vbrief/schemas/ may keep legacy paths; consumer copies must not (#2670).
|
|
62
|
+
*
|
|
63
|
+
* `skipRel` is dest files the collect record plans to replace (#3456 plan-note):
|
|
64
|
+
* never validate stale dest bytes a skipped write would have replaced.
|
|
56
65
|
*/
|
|
57
|
-
export function assertProjectedSchemaDescriptionsRooted(projectDir, destinationDir) {
|
|
66
|
+
export function assertProjectedSchemaDescriptionsRooted(projectDir, destinationDir, options = {}) {
|
|
58
67
|
assertDestinationNotSymlink(projectDir, destinationDir);
|
|
59
68
|
if (!isDirectory(destinationDir))
|
|
60
69
|
return;
|
|
70
|
+
const skipRel = options.skipRel;
|
|
61
71
|
for (const rel of collectSchemaFiles(destinationDir)) {
|
|
72
|
+
if (skipRel?.has(rel))
|
|
73
|
+
continue;
|
|
62
74
|
const full = join(destinationDir, rel);
|
|
63
75
|
const text = readFileSync(full, "utf8");
|
|
64
|
-
|
|
65
|
-
throw new Error(`projected xbrief schema still cites ${LEGACY_EVAL_PATH_PREFIX}: ${join(MIGRATED_ARTIFACT_DIR, "schemas", rel)}`);
|
|
66
|
-
}
|
|
76
|
+
assertPlannedSchemaDescriptionRooted(rel, text);
|
|
67
77
|
}
|
|
68
78
|
}
|
|
69
79
|
function writeFileIfChanged(projectDir, target, content) {
|
|
@@ -99,23 +109,26 @@ export function syncConsumerXbriefSchemas(projectDir, deftDir) {
|
|
|
99
109
|
}
|
|
100
110
|
const destinationDir = join(projectDir, MIGRATED_ARTIFACT_DIR, "schemas");
|
|
101
111
|
assertDestinationNotSymlink(projectDir, destinationDir);
|
|
102
|
-
|
|
112
|
+
containedMkdir({ root: resolve(projectDir), target: destinationDir });
|
|
103
113
|
let changed = false;
|
|
114
|
+
const plannedRel = new Set();
|
|
104
115
|
for (const rel of collectSchemaFiles(sourceDir)) {
|
|
105
116
|
if (rel === OBSOLETE_CORE_SCHEMA)
|
|
106
117
|
continue;
|
|
107
118
|
const source = join(sourceDir, rel);
|
|
108
119
|
const destination = join(destinationDir, rel);
|
|
109
120
|
const projected = rewriteProjectedSchemaContent(readFileSync(source, "utf8"));
|
|
121
|
+
// Planned content is the oracle. Dest bytes a skipped write would replace
|
|
122
|
+
// are not validated (#3456 plan-note).
|
|
123
|
+
assertPlannedSchemaDescriptionRooted(rel, projected);
|
|
124
|
+
plannedRel.add(rel);
|
|
110
125
|
changed = writeFileIfChanged(projectDir, destination, projected) || changed;
|
|
111
126
|
}
|
|
112
127
|
const obsoleteDestination = join(destinationDir, OBSOLETE_CORE_SCHEMA);
|
|
113
|
-
|
|
114
|
-
if (existsSync(obsoleteDestination)) {
|
|
115
|
-
rmSync(obsoleteDestination, { force: true });
|
|
128
|
+
if (containedRemove({ root: projectDir, target: obsoleteDestination }).removed) {
|
|
116
129
|
changed = true;
|
|
117
130
|
}
|
|
118
|
-
assertProjectedSchemaDescriptionsRooted(projectDir, destinationDir);
|
|
131
|
+
assertProjectedSchemaDescriptionsRooted(projectDir, destinationDir, { skipRel: plannedRel });
|
|
119
132
|
return changed;
|
|
120
133
|
}
|
|
121
134
|
function syncBareVersionMarkerWithPolicy(projectDir, version, allowRootFallback) {
|
|
@@ -6,15 +6,61 @@
|
|
|
6
6
|
* the missing derive step.
|
|
7
7
|
*/
|
|
8
8
|
import { type AcceptanceClause } from "../verify-ac/clauses.js";
|
|
9
|
+
/** One-line remediation when a stamp has no statement-traceable clause (#3398). */
|
|
10
|
+
export declare const CLAUSE_STAMP_IMPLEMENTATION_ONLY_REMEDIATION = "derive clauses from the statement's testable constraints";
|
|
11
|
+
export type ClauseProvenance = "statement" | "implementation";
|
|
12
|
+
export interface ProvenanceCounts {
|
|
13
|
+
readonly statement: number;
|
|
14
|
+
readonly implementation: number;
|
|
15
|
+
}
|
|
16
|
+
export interface TracedAcceptanceClause extends AcceptanceClause {
|
|
17
|
+
readonly provenance: ClauseProvenance;
|
|
18
|
+
}
|
|
19
|
+
export interface ClauseStampPreparation {
|
|
20
|
+
readonly ok: boolean;
|
|
21
|
+
readonly clauses: readonly TracedAcceptanceClause[];
|
|
22
|
+
readonly provenance_counts: ProvenanceCounts;
|
|
23
|
+
readonly ambiguity_attestation?: "none_found";
|
|
24
|
+
readonly remediation?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare const MISSING_AMBIGUITY_ATTESTATION_CAUSE: "missing_ambiguity_attestation";
|
|
27
|
+
export declare const AMBIGUITY_ATTESTATION_REMEDIATION = "record ambiguity readings or attest none_found";
|
|
28
|
+
export interface AmbiguityAttestationCheck {
|
|
29
|
+
readonly ok: boolean;
|
|
30
|
+
readonly attested: boolean;
|
|
31
|
+
readonly kind: "ambiguous-clause" | "none_found" | "missing";
|
|
32
|
+
readonly message: string;
|
|
33
|
+
readonly cause?: typeof MISSING_AMBIGUITY_ATTESTATION_CAUSE;
|
|
34
|
+
readonly remediation?: string;
|
|
35
|
+
}
|
|
9
36
|
/** True when activate/promote must run #3323 (absent, empty none_stated, or command-only). */
|
|
10
37
|
export declare function needsClauseDerivation(acceptance: unknown): boolean;
|
|
11
38
|
/** Task-statement text from title, narratives, and item Acceptance fields. */
|
|
12
39
|
export declare function collectTaskStatementFromPlan(plan: Record<string, unknown>): string;
|
|
13
40
|
export interface ClauseDerivationResult {
|
|
14
41
|
readonly applied: boolean;
|
|
15
|
-
readonly clauses: readonly AcceptanceClause[];
|
|
42
|
+
readonly clauses: readonly (AcceptanceClause | TracedAcceptanceClause)[];
|
|
16
43
|
readonly notice: string;
|
|
17
44
|
}
|
|
45
|
+
/** Statement-traceable when the clause text or its identifiers appear in the statement. */
|
|
46
|
+
export declare function traceClauseProvenance(clause: AcceptanceClause, statement: string): ClauseProvenance;
|
|
47
|
+
export declare function countClauseProvenance(clauses: readonly TracedAcceptanceClause[]): ProvenanceCounts;
|
|
48
|
+
/** Fail closed when every clause is implementation-provenance; else attest ambiguity. */
|
|
49
|
+
export declare function prepareClauseStamp(clauses: readonly AcceptanceClause[], statement: string): ClauseStampPreparation;
|
|
50
|
+
/** Config-error surface for verify:ac: attestation or an ambiguous clause with readings. */
|
|
51
|
+
export declare function evaluateAmbiguityAttestation(acceptance: unknown): AmbiguityAttestationCheck;
|
|
52
|
+
/**
|
|
53
|
+
* Fail-closed check for a *stamped* plan.acceptance (#3559).
|
|
54
|
+
* Command-only / empty floor blocks are not stamps; they keep the empty-resolution path.
|
|
55
|
+
*/
|
|
56
|
+
export declare function stampedAmbiguityAttestationError(acceptance: unknown): AmbiguityAttestationCheck | null;
|
|
57
|
+
/**
|
|
58
|
+
* Annotate stamped clauses with provenance and an ambiguity attestation.
|
|
59
|
+
* An all-implementation stamp is stripped (never sufficient alone).
|
|
60
|
+
*/
|
|
61
|
+
export declare function applyClauseQualityToPlan(plan: Record<string, unknown>): ClauseDerivationResult;
|
|
62
|
+
/** Ingest path: strip an implementation-only stamp and record the remediation. */
|
|
63
|
+
export declare function applyClauseQualityForIngest(plan: Record<string, unknown>): ClauseDerivationResult;
|
|
18
64
|
/**
|
|
19
65
|
* Stamp derived clauses onto plan.acceptance in place. Headless: records
|
|
20
66
|
* chosen_reading, never blocks on a question.
|
|
@@ -5,8 +5,12 @@
|
|
|
5
5
|
* brief by hand; #3334 only required plan.acceptance to exist. This module is
|
|
6
6
|
* the missing derive step.
|
|
7
7
|
*/
|
|
8
|
-
import { ENV_RUN_SUMMARY_PATH, RunSummaryEmitter } from "../run-summary/index.js";
|
|
8
|
+
import { ENV_RUN_SUMMARY_PATH, RunSummaryEmitter, } from "../run-summary/index.js";
|
|
9
9
|
import { deriveAcceptanceClauses, readAcceptanceClauses, serializeAcceptanceClauses, } from "../verify-ac/clauses.js";
|
|
10
|
+
/** One-line remediation when a stamp has no statement-traceable clause (#3398). */
|
|
11
|
+
export const CLAUSE_STAMP_IMPLEMENTATION_ONLY_REMEDIATION = "derive clauses from the statement's testable constraints";
|
|
12
|
+
export const MISSING_AMBIGUITY_ATTESTATION_CAUSE = "missing_ambiguity_attestation";
|
|
13
|
+
export const AMBIGUITY_ATTESTATION_REMEDIATION = "record ambiguity readings or attest none_found";
|
|
10
14
|
const NARRATIVE_KEYS = [
|
|
11
15
|
"Overview",
|
|
12
16
|
"Description",
|
|
@@ -76,6 +80,194 @@ export function collectTaskStatementFromPlan(plan) {
|
|
|
76
80
|
}
|
|
77
81
|
return parts.join("\n\n");
|
|
78
82
|
}
|
|
83
|
+
function hasAmbiguousReadings(clause) {
|
|
84
|
+
return clause.ambiguous === true && (clause.readings?.length ?? 0) > 0;
|
|
85
|
+
}
|
|
86
|
+
function extractClauseTokens(text) {
|
|
87
|
+
const found = [];
|
|
88
|
+
const seen = new Set();
|
|
89
|
+
const push = (raw) => {
|
|
90
|
+
const token = raw.trim();
|
|
91
|
+
if (token.length < 2 || seen.has(token)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
seen.add(token);
|
|
95
|
+
found.push(token);
|
|
96
|
+
};
|
|
97
|
+
const quoted = /["'`]([^"'`\n]{2,80})["'`]/g;
|
|
98
|
+
let match = quoted.exec(text);
|
|
99
|
+
while (match !== null) {
|
|
100
|
+
push(match[1] ?? "");
|
|
101
|
+
match = quoted.exec(text);
|
|
102
|
+
}
|
|
103
|
+
const pathRe = /(?<![A-Za-z0-9_])((?:[A-Za-z0-9_.-]+\/)+[A-Za-z0-9_.-]+\.[A-Za-z][A-Za-z0-9]+)(?![A-Za-z0-9_])/g;
|
|
104
|
+
match = pathRe.exec(text);
|
|
105
|
+
while (match !== null) {
|
|
106
|
+
push(match[1] ?? "");
|
|
107
|
+
match = pathRe.exec(text);
|
|
108
|
+
}
|
|
109
|
+
const ident = /(?<![A-Za-z0-9_])([A-Za-z_][A-Za-z0-9_]*)(?![A-Za-z0-9_])/g;
|
|
110
|
+
match = ident.exec(text);
|
|
111
|
+
while (match !== null) {
|
|
112
|
+
const token = match[1] ?? "";
|
|
113
|
+
const camelOrPascal = /[a-z][A-Z]/.test(token) || /[A-Z][a-z]+[A-Z]/.test(token);
|
|
114
|
+
const codeShaped = camelOrPascal || token.includes("_");
|
|
115
|
+
if (codeShaped) {
|
|
116
|
+
push(token);
|
|
117
|
+
}
|
|
118
|
+
match = ident.exec(text);
|
|
119
|
+
}
|
|
120
|
+
return found;
|
|
121
|
+
}
|
|
122
|
+
/** Statement-traceable when the clause text or its identifiers appear in the statement. */
|
|
123
|
+
export function traceClauseProvenance(clause, statement) {
|
|
124
|
+
const normClause = clause.text.replace(/\s+/g, " ").trim();
|
|
125
|
+
const normStatement = statement.replace(/\s+/g, " ").trim();
|
|
126
|
+
if (normClause.length > 0 && normStatement.includes(normClause)) {
|
|
127
|
+
return "statement";
|
|
128
|
+
}
|
|
129
|
+
const tokens = extractClauseTokens(clause.text);
|
|
130
|
+
if (tokens.length === 0) {
|
|
131
|
+
return "implementation";
|
|
132
|
+
}
|
|
133
|
+
return tokens.every((token) => statement.includes(token)) ? "statement" : "implementation";
|
|
134
|
+
}
|
|
135
|
+
export function countClauseProvenance(clauses) {
|
|
136
|
+
let statement = 0;
|
|
137
|
+
let implementation = 0;
|
|
138
|
+
for (const clause of clauses) {
|
|
139
|
+
if (clause.provenance === "statement") {
|
|
140
|
+
statement += 1;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
implementation += 1;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { statement, implementation };
|
|
147
|
+
}
|
|
148
|
+
/** Fail closed when every clause is implementation-provenance; else attest ambiguity. */
|
|
149
|
+
export function prepareClauseStamp(clauses, statement) {
|
|
150
|
+
const traced = clauses.map((clause) => ({
|
|
151
|
+
...clause,
|
|
152
|
+
provenance: traceClauseProvenance(clause, statement),
|
|
153
|
+
}));
|
|
154
|
+
const provenance_counts = countClauseProvenance(traced);
|
|
155
|
+
if (traced.length > 0 && provenance_counts.statement === 0) {
|
|
156
|
+
return {
|
|
157
|
+
ok: false,
|
|
158
|
+
clauses: traced,
|
|
159
|
+
provenance_counts,
|
|
160
|
+
remediation: CLAUSE_STAMP_IMPLEMENTATION_ONLY_REMEDIATION,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
ok: true,
|
|
165
|
+
clauses: traced,
|
|
166
|
+
provenance_counts,
|
|
167
|
+
ambiguity_attestation: traced.some(hasAmbiguousReadings) ? undefined : "none_found",
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const AMBIGUITY_ATTESTATION_MISSING = `verify:ac config error: missing ambiguity attestation; ${AMBIGUITY_ATTESTATION_REMEDIATION}`;
|
|
171
|
+
function missingAttestationCheck() {
|
|
172
|
+
return {
|
|
173
|
+
ok: false,
|
|
174
|
+
attested: false,
|
|
175
|
+
kind: "missing",
|
|
176
|
+
message: AMBIGUITY_ATTESTATION_MISSING,
|
|
177
|
+
cause: MISSING_AMBIGUITY_ATTESTATION_CAUSE,
|
|
178
|
+
remediation: AMBIGUITY_ATTESTATION_REMEDIATION,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/** Config-error surface for verify:ac: attestation or an ambiguous clause with readings. */
|
|
182
|
+
export function evaluateAmbiguityAttestation(acceptance) {
|
|
183
|
+
const rec = asRecord(acceptance);
|
|
184
|
+
if (rec === null) {
|
|
185
|
+
return missingAttestationCheck();
|
|
186
|
+
}
|
|
187
|
+
const clauses = readAcceptanceClauses(rec);
|
|
188
|
+
if (clauses.some(hasAmbiguousReadings)) {
|
|
189
|
+
return { ok: true, attested: true, kind: "ambiguous-clause", message: "" };
|
|
190
|
+
}
|
|
191
|
+
if (rec.ambiguity_attestation === "none_found") {
|
|
192
|
+
return { ok: true, attested: true, kind: "none_found", message: "" };
|
|
193
|
+
}
|
|
194
|
+
return missingAttestationCheck();
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Fail-closed check for a *stamped* plan.acceptance (#3559).
|
|
198
|
+
* Command-only / empty floor blocks are not stamps; they keep the empty-resolution path.
|
|
199
|
+
*/
|
|
200
|
+
export function stampedAmbiguityAttestationError(acceptance) {
|
|
201
|
+
const rec = asRecord(acceptance);
|
|
202
|
+
if (rec === null) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
if (readAcceptanceClauses(rec).length === 0) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
const check = evaluateAmbiguityAttestation(rec);
|
|
209
|
+
return check.ok ? null : check;
|
|
210
|
+
}
|
|
211
|
+
function serializeTracedClauses(clauses) {
|
|
212
|
+
return serializeAcceptanceClauses(clauses).map((row, index) => ({
|
|
213
|
+
...row,
|
|
214
|
+
provenance: clauses[index]?.provenance ?? "implementation",
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Annotate stamped clauses with provenance and an ambiguity attestation.
|
|
219
|
+
* An all-implementation stamp is stripped (never sufficient alone).
|
|
220
|
+
*/
|
|
221
|
+
export function applyClauseQualityToPlan(plan) {
|
|
222
|
+
const existing = asRecord(plan.acceptance);
|
|
223
|
+
if (existing === null) {
|
|
224
|
+
return { applied: false, clauses: [], notice: "" };
|
|
225
|
+
}
|
|
226
|
+
const clauses = readAcceptanceClauses(existing);
|
|
227
|
+
if (clauses.length === 0) {
|
|
228
|
+
return { applied: false, clauses: [], notice: "" };
|
|
229
|
+
}
|
|
230
|
+
const prepared = prepareClauseStamp(clauses, collectTaskStatementFromPlan(plan));
|
|
231
|
+
if (!prepared.ok) {
|
|
232
|
+
const next = { ...existing };
|
|
233
|
+
delete next.clauses;
|
|
234
|
+
plan.acceptance = next;
|
|
235
|
+
return {
|
|
236
|
+
applied: false,
|
|
237
|
+
clauses: prepared.clauses,
|
|
238
|
+
notice: prepared.remediation ?? CLAUSE_STAMP_IMPLEMENTATION_ONLY_REMEDIATION,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const next = {
|
|
242
|
+
...existing,
|
|
243
|
+
clauses: serializeTracedClauses(prepared.clauses),
|
|
244
|
+
};
|
|
245
|
+
if (prepared.ambiguity_attestation !== undefined &&
|
|
246
|
+
existing.ambiguity_attestation === undefined) {
|
|
247
|
+
next.ambiguity_attestation = prepared.ambiguity_attestation;
|
|
248
|
+
}
|
|
249
|
+
plan.acceptance = next;
|
|
250
|
+
return {
|
|
251
|
+
applied: true,
|
|
252
|
+
clauses: prepared.clauses,
|
|
253
|
+
notice: formatAmbiguousClauseNotice(prepared.clauses),
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/** Ingest path: strip an implementation-only stamp and record the remediation. */
|
|
257
|
+
export function applyClauseQualityForIngest(plan) {
|
|
258
|
+
const quality = applyClauseQualityToPlan(plan);
|
|
259
|
+
if (!quality.applied && quality.notice.length > 0) {
|
|
260
|
+
const existing = asRecord(plan.acceptance);
|
|
261
|
+
if (existing !== null) {
|
|
262
|
+
plan.acceptance = {
|
|
263
|
+
...existing,
|
|
264
|
+
derived_reason: quality.notice,
|
|
265
|
+
quality_notice: quality.notice,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return quality;
|
|
270
|
+
}
|
|
79
271
|
function formatAmbiguousClauseNotice(clauses) {
|
|
80
272
|
const flagged = clauses.filter((clause) => clause.ambiguous);
|
|
81
273
|
const lines = [
|
|
@@ -117,6 +309,7 @@ export function applyClauseDerivationToPlan(plan, options = {}) {
|
|
|
117
309
|
? existing.source_rung
|
|
118
310
|
: "stated"
|
|
119
311
|
: "derived";
|
|
312
|
+
const previousAcceptance = plan.acceptance;
|
|
120
313
|
plan.acceptance = {
|
|
121
314
|
...(existing ?? {}),
|
|
122
315
|
commands: hasCommands ? commands : [],
|
|
@@ -125,13 +318,32 @@ export function applyClauseDerivationToPlan(plan, options = {}) {
|
|
|
125
318
|
derived_reason: `derived ${clauses.length} independently testable clauses from the task statement before product edit (#3323)`,
|
|
126
319
|
clauses: serializeAcceptanceClauses(clauses),
|
|
127
320
|
};
|
|
321
|
+
const quality = applyClauseQualityToPlan(plan);
|
|
322
|
+
if (!quality.applied) {
|
|
323
|
+
const existing = asRecord(previousAcceptance);
|
|
324
|
+
if (existing !== null && quality.notice.length > 0) {
|
|
325
|
+
plan.acceptance = {
|
|
326
|
+
...existing,
|
|
327
|
+
quality_notice: quality.notice,
|
|
328
|
+
derived_reason: quality.notice,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
plan.acceptance = previousAcceptance;
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
applied: false,
|
|
336
|
+
clauses: quality.clauses,
|
|
337
|
+
notice: quality.notice,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
128
340
|
if (options.emitStamp !== false && options.projectRoot !== undefined) {
|
|
129
341
|
emitAcceptanceStampFromPlan(options.projectRoot, plan);
|
|
130
342
|
}
|
|
131
343
|
return {
|
|
132
344
|
applied: true,
|
|
133
|
-
clauses,
|
|
134
|
-
notice:
|
|
345
|
+
clauses: quality.clauses,
|
|
346
|
+
notice: quality.notice,
|
|
135
347
|
};
|
|
136
348
|
}
|
|
137
349
|
/** Stable fingerprint of plan.acceptance for first-write / material-change detection (#3355). */
|
|
@@ -176,18 +388,35 @@ export function emitAcceptanceStampFromPlan(projectRoot, plan, env = process.env
|
|
|
176
388
|
return;
|
|
177
389
|
}
|
|
178
390
|
try {
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
391
|
+
const emitter = new RunSummaryEmitter({
|
|
392
|
+
projectRoot,
|
|
393
|
+
env,
|
|
394
|
+
component: "clause-derivation",
|
|
395
|
+
});
|
|
183
396
|
const commands = Array.isArray(acceptance.commands) ? acceptance.commands.length : 0;
|
|
184
397
|
const clauses = Array.isArray(acceptance.clauses) ? acceptance.clauses.length : 0;
|
|
185
|
-
|
|
398
|
+
const statement = collectTaskStatementFromPlan(rec);
|
|
399
|
+
const rawRows = Array.isArray(acceptance.clauses) ? acceptance.clauses : [];
|
|
400
|
+
const traced = readAcceptanceClauses(acceptance).map((clause, index) => {
|
|
401
|
+
const row = asRecord(rawRows[index]);
|
|
402
|
+
const stamped = row?.provenance === "statement" || row?.provenance === "implementation"
|
|
403
|
+
? row.provenance
|
|
404
|
+
: undefined;
|
|
405
|
+
return {
|
|
406
|
+
...clause,
|
|
407
|
+
provenance: stamped ??
|
|
408
|
+
(statement.trim().length > 0 ? traceClauseProvenance(clause, statement) : "statement"),
|
|
409
|
+
};
|
|
410
|
+
});
|
|
411
|
+
const payload = {
|
|
186
412
|
rung: typeof acceptance.source_rung === "string" ? acceptance.source_rung : "project_floor",
|
|
187
413
|
none_stated: acceptance.none_stated === true,
|
|
188
414
|
command_count: commands,
|
|
189
415
|
clause_count: clauses,
|
|
190
|
-
|
|
416
|
+
provenance_counts: countClauseProvenance(traced),
|
|
417
|
+
ambiguity_attested: evaluateAmbiguityAttestation(acceptance).attested,
|
|
418
|
+
};
|
|
419
|
+
emitter.emitAcceptanceStamp(payload);
|
|
191
420
|
}
|
|
192
421
|
catch {
|
|
193
422
|
// fail-open
|
|
@@ -58,6 +58,8 @@ export declare function buildIssueVbrief(issue: Record<string, unknown>, status:
|
|
|
58
58
|
infoRootKey?: typeof LEGACY_INFO_ROOT_KEY | typeof MIGRATED_INFO_ROOT_KEY;
|
|
59
59
|
infoVersion?: string;
|
|
60
60
|
}): [Record<string, unknown>, string];
|
|
61
|
+
/** Include a refused-stamp remediation on the ingest result so it is not silent. */
|
|
62
|
+
export declare function formatIngestCreatedMessage(folder: string, filename: string, plan: unknown, dryRun?: boolean): string;
|
|
61
63
|
export declare function targetFilename(number: number, title: string, artifactSuffix?: typeof LEGACY_ARTIFACT_SUFFIX | typeof MIGRATED_ARTIFACT_SUFFIX): string;
|
|
62
64
|
export interface FetchIssueOptions {
|
|
63
65
|
readonly cwd?: string | null;
|
|
@@ -116,5 +118,5 @@ export declare function ingestSingleForAccept(n: number, repo: string, options?:
|
|
|
116
118
|
projectRoot?: string | null;
|
|
117
119
|
status?: IngestStatus;
|
|
118
120
|
cacheRoot?: string | null;
|
|
119
|
-
}): [IngestResult, string | null];
|
|
121
|
+
}): [IngestResult, string | null, string];
|
|
120
122
|
//# sourceMappingURL=issue-ingest.d.ts.map
|
|
@@ -5,6 +5,7 @@ import { scan } from "../cache/scanner.js";
|
|
|
5
5
|
import { assertWriteTargetSafe, ProjectionContainmentError } from "../fs/projection-containment.js";
|
|
6
6
|
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
7
7
|
import { captureAndAttachLiteralAcceptance } from "../literal-acceptance/index.js";
|
|
8
|
+
import { stampIntendedPlacement } from "../preflight/intended-placement.js";
|
|
8
9
|
import { stampAcceptanceFromLiteralCapture } from "../product-first-done-gate/index.js";
|
|
9
10
|
import { call } from "../scm/call.js";
|
|
10
11
|
import { resolveProjectRoot } from "../scope/project-context.js";
|
|
@@ -14,7 +15,7 @@ import { slugify, TODAY } from "../vbrief-build/build.js";
|
|
|
14
15
|
import { EMITTED_VBRIEF_VERSION } from "../vbrief-build/constants.js";
|
|
15
16
|
import { stampDerivedClausesOnAcceptance } from "../verify-ac/clauses.js";
|
|
16
17
|
import { LEGACY_ARTIFACT_SUFFIX, LEGACY_INFO_ROOT_KEY, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY, VBRIEF_VERSION, } from "../xbrief-migrate/constants.js";
|
|
17
|
-
import { emitAcceptanceStampFromPlan } from "./clause-derivation.js";
|
|
18
|
+
import { applyClauseQualityForIngest, emitAcceptanceStampFromPlan } from "./clause-derivation.js";
|
|
18
19
|
import { findAcHeading, parseCheckboxItems, parseListItems, sliceAcSection, stripCodeBlocks, stripFencedCodeBlocks, } from "./markdown-scanners.js";
|
|
19
20
|
import { detectRepo, extractReferencesFromVbrief, fetchOpenIssues, GITHUB_ISSUE_REF_TYPES, LIFECYCLE_FOLDERS, parseIssueNumber, } from "./reconcile-issues.js";
|
|
20
21
|
/** Reference type pointing at the canonical current-shape comment permalink (#1870). */
|
|
@@ -480,11 +481,13 @@ export function buildIssueVbrief(issue, status, repoUrl, options = {}) {
|
|
|
480
481
|
// #3323: when no commands were stated, derive numbered clauses before product edit.
|
|
481
482
|
const derived = stampDerivedClausesOnAcceptance(plan, [title, overviewSource].filter((s) => s.length > 0).join("\n\n"));
|
|
482
483
|
Object.assign(plan, derived.plan);
|
|
484
|
+
applyClauseQualityForIngest(plan);
|
|
483
485
|
}
|
|
484
486
|
else {
|
|
485
487
|
// No body: still record none_stated acceptance so absence is a decision.
|
|
486
488
|
Object.assign(plan, stampAcceptanceFromLiteralCapture(plan));
|
|
487
489
|
}
|
|
490
|
+
stampIntendedPlacement(plan);
|
|
488
491
|
const infoRootKey = options.infoRootKey ?? LEGACY_INFO_ROOT_KEY;
|
|
489
492
|
const infoVersion = options.infoVersion ?? EMITTED_VBRIEF_VERSION;
|
|
490
493
|
const briefLabel = infoRootKey === MIGRATED_INFO_ROOT_KEY ? "xBRIEF" : "vBRIEF";
|
|
@@ -499,6 +502,24 @@ export function buildIssueVbrief(issue, status, repoUrl, options = {}) {
|
|
|
499
502
|
folder,
|
|
500
503
|
];
|
|
501
504
|
}
|
|
505
|
+
/** Include a refused-stamp remediation on the ingest result so it is not silent. */
|
|
506
|
+
export function formatIngestCreatedMessage(folder, filename, plan, dryRun = false) {
|
|
507
|
+
const lead = dryRun
|
|
508
|
+
? `DRY-RUN would write ${folder}/${filename}`
|
|
509
|
+
: `CREATED ${folder}/${filename}`;
|
|
510
|
+
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
511
|
+
return lead;
|
|
512
|
+
}
|
|
513
|
+
const acceptance = plan.acceptance;
|
|
514
|
+
if (typeof acceptance !== "object" || acceptance === null || Array.isArray(acceptance)) {
|
|
515
|
+
return lead;
|
|
516
|
+
}
|
|
517
|
+
const notice = acceptance.quality_notice;
|
|
518
|
+
if (typeof notice !== "string" || notice.trim().length === 0) {
|
|
519
|
+
return lead;
|
|
520
|
+
}
|
|
521
|
+
return `${lead}\n${notice.trim()}`;
|
|
522
|
+
}
|
|
502
523
|
export function targetFilename(number, title, artifactSuffix = LEGACY_ARTIFACT_SUFFIX) {
|
|
503
524
|
const slug = slugify(title) || `issue-${number}`;
|
|
504
525
|
return `${TODAY}-${number}-${slug}${artifactSuffix}`;
|
|
@@ -707,7 +728,7 @@ export function ingestOne(issue, options) {
|
|
|
707
728
|
const folderPath = join(options.vbriefDir, folder);
|
|
708
729
|
const target = join(folderPath, filename);
|
|
709
730
|
if (options.dryRun) {
|
|
710
|
-
return ["dryrun", target,
|
|
731
|
+
return ["dryrun", target, formatIngestCreatedMessage(folder, filename, vbrief.plan, true)];
|
|
711
732
|
}
|
|
712
733
|
// Gate lifecycle folder + leaf before mkdir/write so folder/parent symlinks
|
|
713
734
|
// cannot divert issue:ingest / triage:accept outside the project (#2869).
|
|
@@ -724,7 +745,7 @@ export function ingestOne(issue, options) {
|
|
|
724
745
|
mkdirSync(folderPath, { recursive: true });
|
|
725
746
|
writeFileSync(target, `${JSON.stringify(vbrief, null, 2)}\n`, "utf8");
|
|
726
747
|
emitAcceptanceStampFromPlan(projectRoot, vbrief.plan);
|
|
727
|
-
return ["created", target,
|
|
748
|
+
return ["created", target, formatIngestCreatedMessage(folder, filename, vbrief.plan)];
|
|
728
749
|
}
|
|
729
750
|
export function ingestBulk(issues, options) {
|
|
730
751
|
let filtered = issues;
|
|
@@ -753,6 +774,7 @@ export function ingestBulk(issues, options) {
|
|
|
753
774
|
duplicate: [],
|
|
754
775
|
dryrun: [],
|
|
755
776
|
failed: [],
|
|
777
|
+
notices: [],
|
|
756
778
|
};
|
|
757
779
|
for (const issue of filtered) {
|
|
758
780
|
let ingested;
|
|
@@ -770,9 +792,12 @@ export function ingestBulk(issues, options) {
|
|
|
770
792
|
}
|
|
771
793
|
throw exc;
|
|
772
794
|
}
|
|
773
|
-
const [result, path,
|
|
795
|
+
const [result, path, msg] = ingested;
|
|
774
796
|
const rel = path !== null ? path.replace(`${options.vbriefDir}/`, "").replace(/\\/g, "/") : "";
|
|
775
797
|
summary[result].push(rel);
|
|
798
|
+
if (msg.includes("\n")) {
|
|
799
|
+
summary.notices.push(msg);
|
|
800
|
+
}
|
|
776
801
|
if (result === "created" && path !== null) {
|
|
777
802
|
const num = Number(issue.number);
|
|
778
803
|
const existing = refs.get(num) ?? [];
|
|
@@ -840,6 +865,9 @@ export function issueIngestMain(args) {
|
|
|
840
865
|
for (const entry of created) {
|
|
841
866
|
process.stdout.write(` CREATED ${entry}\n`);
|
|
842
867
|
}
|
|
868
|
+
for (const notice of summary.notices ?? []) {
|
|
869
|
+
process.stdout.write(`${notice}\n`);
|
|
870
|
+
}
|
|
843
871
|
for (const entry of dryrun) {
|
|
844
872
|
process.stdout.write(` DRY-RUN ${entry}\n`);
|
|
845
873
|
}
|
|
@@ -890,12 +918,13 @@ export function ingestSingleForAccept(n, repo, options = {}) {
|
|
|
890
918
|
if (issue === null) {
|
|
891
919
|
throw new Error(`failed to fetch GitHub issue #${n} from ${repo} (unified cache miss + live gh api fetch failed; see stderr)`);
|
|
892
920
|
}
|
|
893
|
-
|
|
921
|
+
// Forward ingestOne's notice-bearing third return so triage:accept can
|
|
922
|
+
// print a refused-stamp remediation instead of only the decision id (#3398).
|
|
923
|
+
return ingestOne(issue, {
|
|
894
924
|
vbriefDir,
|
|
895
925
|
status: options.status ?? "proposed",
|
|
896
926
|
repoUrl,
|
|
897
927
|
cwd: root,
|
|
898
928
|
});
|
|
899
|
-
return [result, path];
|
|
900
929
|
}
|
|
901
930
|
//# sourceMappingURL=issue-ingest.js.map
|
|
@@ -25,6 +25,11 @@ export interface EvaluateCompletedTrackedOptions {
|
|
|
25
25
|
readonly repo?: string | null;
|
|
26
26
|
/** Explicit delivery tip ref (e.g. origin/master). Overrides policy/git default. */
|
|
27
27
|
readonly tip?: string | null;
|
|
28
|
+
/**
|
|
29
|
+
* When set, check only this issue number (#3476 drive-to DONE).
|
|
30
|
+
* Sibling unlanded closed issues do not fail the per-issue scan.
|
|
31
|
+
*/
|
|
32
|
+
readonly issue?: number | null;
|
|
28
33
|
readonly runGh?: RunGhFn;
|
|
29
34
|
readonly skipGh?: boolean;
|
|
30
35
|
readonly runGit?: GitRunner;
|