@deftai/directive-runtime 0.129.6 → 0.129.8
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/Taskfile.yml +103 -16
- package/commands.md +8 -3
- package/dist/session-start.d.ts +8 -2
- package/dist/session-start.js +33 -12
- package/dist/vbrief-preflight.d.ts +4 -0
- package/dist/vbrief-preflight.js +34 -0
- package/dist/verify-scope-provenance.d.ts +13 -1
- package/dist/verify-scope-provenance.js +44 -1
- package/docs/getting-started.md +2 -0
- package/docs/scope-provenance.md +15 -1
- package/package.json +1 -1
- package/packs/skills/skills-pack-0.1.json +1 -1
- package/scm/github.md +5 -0
- package/skills/deft-directive-review-cycle/SKILL.md +15 -6
- package/skills/deft-directive-swarm/references/host-claude-code.md +11 -1
- package/skills/deft-directive-swarm/references/host-grok-build.md +3 -2
- package/vendor/directive-content/Taskfile.yml +103 -16
- package/vendor/directive-content/commands.md +8 -3
- package/vendor/directive-content/docs/getting-started.md +2 -0
- package/vendor/directive-content/docs/scope-provenance.md +15 -1
- package/vendor/directive-content/packs/skills/skills-pack-0.1.json +1 -1
- package/vendor/directive-content/scm/github.md +5 -0
- package/vendor/directive-content/skills/deft-directive-review-cycle/SKILL.md +15 -6
- package/vendor/directive-content/skills/deft-directive-swarm/references/host-claude-code.md +11 -1
- package/vendor/directive-content/skills/deft-directive-swarm/references/host-grok-build.md +3 -2
- package/vendor/directive-core/dist/.tsbuildinfo +1 -1
- package/vendor/directive-core/dist/check/context.d.ts.map +1 -1
- package/vendor/directive-core/dist/check/context.js +3 -31
- package/vendor/directive-core/dist/check/context.js.map +1 -1
- package/vendor/directive-core/dist/check/gate-lists.d.ts.map +1 -1
- package/vendor/directive-core/dist/check/gate-lists.js +4 -32
- package/vendor/directive-core/dist/check/gate-lists.js.map +1 -1
- package/vendor/directive-core/dist/check/walk-test-files.d.ts +3 -0
- package/vendor/directive-core/dist/check/walk-test-files.d.ts.map +1 -0
- package/vendor/directive-core/dist/check/walk-test-files.js +67 -0
- package/vendor/directive-core/dist/check/walk-test-files.js.map +1 -0
- package/vendor/directive-core/dist/fs/contained-write.d.ts +8 -2
- package/vendor/directive-core/dist/fs/contained-write.d.ts.map +1 -1
- package/vendor/directive-core/dist/fs/contained-write.js +22 -6
- package/vendor/directive-core/dist/fs/contained-write.js.map +1 -1
- package/vendor/directive-core/dist/hooks/dispatcher.d.ts +7 -1
- package/vendor/directive-core/dist/hooks/dispatcher.d.ts.map +1 -1
- package/vendor/directive-core/dist/hooks/dispatcher.js +180 -10
- package/vendor/directive-core/dist/hooks/dispatcher.js.map +1 -1
- package/vendor/directive-core/dist/hooks/scope.d.ts +54 -0
- package/vendor/directive-core/dist/hooks/scope.d.ts.map +1 -1
- package/vendor/directive-core/dist/hooks/scope.js +83 -2
- package/vendor/directive-core/dist/hooks/scope.js.map +1 -1
- package/vendor/directive-core/dist/init-deposit/agent-hooks.d.ts +26 -1
- package/vendor/directive-core/dist/init-deposit/agent-hooks.d.ts.map +1 -1
- package/vendor/directive-core/dist/init-deposit/agent-hooks.js +52 -3
- package/vendor/directive-core/dist/init-deposit/agent-hooks.js.map +1 -1
- package/vendor/directive-core/dist/init-deposit/host-hook-outer.d.ts +43 -0
- package/vendor/directive-core/dist/init-deposit/host-hook-outer.d.ts.map +1 -1
- package/vendor/directive-core/dist/init-deposit/host-hook-outer.js +148 -0
- package/vendor/directive-core/dist/init-deposit/host-hook-outer.js.map +1 -1
- package/vendor/directive-core/dist/intake/reconcile-issues.d.ts +33 -1
- package/vendor/directive-core/dist/intake/reconcile-issues.d.ts.map +1 -1
- package/vendor/directive-core/dist/intake/reconcile-issues.js +288 -12
- package/vendor/directive-core/dist/intake/reconcile-issues.js.map +1 -1
- package/vendor/directive-core/dist/intake/thin-brief.d.ts +78 -0
- package/vendor/directive-core/dist/intake/thin-brief.d.ts.map +1 -0
- package/vendor/directive-core/dist/intake/thin-brief.js +224 -0
- package/vendor/directive-core/dist/intake/thin-brief.js.map +1 -0
- package/vendor/directive-core/dist/policy/ceremony-dial.d.ts.map +1 -1
- package/vendor/directive-core/dist/policy/ceremony-dial.js +2 -1
- package/vendor/directive-core/dist/policy/ceremony-dial.js.map +1 -1
- package/vendor/directive-core/dist/policy/small-change-lane.d.ts +193 -0
- package/vendor/directive-core/dist/policy/small-change-lane.d.ts.map +1 -0
- package/vendor/directive-core/dist/policy/small-change-lane.js +690 -0
- package/vendor/directive-core/dist/policy/small-change-lane.js.map +1 -0
- package/vendor/directive-core/dist/pr-merge-readiness/slizard-gate.d.ts +10 -5
- package/vendor/directive-core/dist/pr-merge-readiness/slizard-gate.d.ts.map +1 -1
- package/vendor/directive-core/dist/pr-merge-readiness/slizard-gate.js +41 -18
- package/vendor/directive-core/dist/pr-merge-readiness/slizard-gate.js.map +1 -1
- package/vendor/directive-core/dist/preflight/evaluate.d.ts +3 -0
- package/vendor/directive-core/dist/preflight/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/preflight/evaluate.js +15 -0
- package/vendor/directive-core/dist/preflight/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/preflight-gh/pre-push.d.ts +3 -2
- package/vendor/directive-core/dist/preflight-gh/pre-push.d.ts.map +1 -1
- package/vendor/directive-core/dist/preflight-gh/pre-push.js +19 -18
- package/vendor/directive-core/dist/preflight-gh/pre-push.js.map +1 -1
- package/vendor/directive-core/dist/product-first-done-gate/evaluate.d.ts +6 -0
- package/vendor/directive-core/dist/product-first-done-gate/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/product-first-done-gate/evaluate.js +26 -0
- package/vendor/directive-core/dist/product-first-done-gate/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/scope/decomposed-refs.d.ts +7 -0
- package/vendor/directive-core/dist/scope/decomposed-refs.d.ts.map +1 -1
- package/vendor/directive-core/dist/scope/decomposed-refs.js +27 -10
- package/vendor/directive-core/dist/scope/decomposed-refs.js.map +1 -1
- package/vendor/directive-core/dist/scope/delivery-evidence.d.ts +218 -19
- package/vendor/directive-core/dist/scope/delivery-evidence.d.ts.map +1 -1
- package/vendor/directive-core/dist/scope/delivery-evidence.js +973 -107
- package/vendor/directive-core/dist/scope/delivery-evidence.js.map +1 -1
- package/vendor/directive-core/dist/scope/transition.d.ts +7 -1
- package/vendor/directive-core/dist/scope/transition.d.ts.map +1 -1
- package/vendor/directive-core/dist/scope/transition.js +45 -5
- package/vendor/directive-core/dist/scope/transition.js.map +1 -1
- package/vendor/directive-core/dist/scope-provenance/base-fence.d.ts +13 -3
- package/vendor/directive-core/dist/scope-provenance/base-fence.d.ts.map +1 -1
- package/vendor/directive-core/dist/scope-provenance/base-fence.js +25 -2
- package/vendor/directive-core/dist/scope-provenance/base-fence.js.map +1 -1
- package/vendor/directive-core/dist/scope-provenance/evaluate.d.ts +12 -0
- package/vendor/directive-core/dist/scope-provenance/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/scope-provenance/evaluate.js +41 -5
- package/vendor/directive-core/dist/scope-provenance/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/scope-provenance/forge-approval.d.ts +106 -0
- package/vendor/directive-core/dist/scope-provenance/forge-approval.d.ts.map +1 -0
- package/vendor/directive-core/dist/scope-provenance/forge-approval.js +382 -0
- package/vendor/directive-core/dist/scope-provenance/forge-approval.js.map +1 -0
- package/vendor/directive-core/dist/session/orientation-compression.d.ts +18 -2
- package/vendor/directive-core/dist/session/orientation-compression.d.ts.map +1 -1
- package/vendor/directive-core/dist/session/orientation-compression.js +38 -5
- package/vendor/directive-core/dist/session/orientation-compression.js.map +1 -1
- package/vendor/directive-core/dist/session/session-start.d.ts +47 -2
- package/vendor/directive-core/dist/session/session-start.d.ts.map +1 -1
- package/vendor/directive-core/dist/session/session-start.js +149 -1
- package/vendor/directive-core/dist/session/session-start.js.map +1 -1
- package/vendor/directive-core/dist/swarm/approach1-babysitter.d.ts +18 -4
- package/vendor/directive-core/dist/swarm/approach1-babysitter.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/approach1-babysitter.js +28 -4
- package/vendor/directive-core/dist/swarm/approach1-babysitter.js.map +1 -1
- package/vendor/directive-core/dist/swarm/finalize-cohort.d.ts +48 -0
- package/vendor/directive-core/dist/swarm/finalize-cohort.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/finalize-cohort.js +175 -19
- package/vendor/directive-core/dist/swarm/finalize-cohort.js.map +1 -1
- package/vendor/directive-core/dist/swarm/finalize-owed.d.ts +18 -3
- package/vendor/directive-core/dist/swarm/finalize-owed.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/finalize-owed.js +139 -32
- package/vendor/directive-core/dist/swarm/finalize-owed.js.map +1 -1
- package/vendor/directive-core/dist/swarm/launch.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/launch.js +4 -8
- package/vendor/directive-core/dist/swarm/launch.js.map +1 -1
- package/vendor/directive-core/dist/swarm/worker-auth-assignment.d.ts +7 -0
- package/vendor/directive-core/dist/swarm/worker-auth-assignment.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/worker-auth-assignment.js +16 -1
- package/vendor/directive-core/dist/swarm/worker-auth-assignment.js.map +1 -1
- package/vendor/directive-core/dist/task-surface/index.d.ts.map +1 -1
- package/vendor/directive-core/dist/task-surface/index.js +41 -0
- package/vendor/directive-core/dist/task-surface/index.js.map +1 -1
- package/vendor/directive-core/dist/triage/evaluate/worktrees.d.ts.map +1 -1
- package/vendor/directive-core/dist/triage/evaluate/worktrees.js +219 -60
- package/vendor/directive-core/dist/triage/evaluate/worktrees.js.map +1 -1
- package/vendor/directive-core/dist/vbrief-reconcile/types.d.ts +5 -1
- package/vendor/directive-core/dist/vbrief-reconcile/types.d.ts.map +1 -1
- package/vendor/directive-core/dist/vbrief-reconcile/umbrellas.d.ts.map +1 -1
- package/vendor/directive-core/dist/vbrief-reconcile/umbrellas.js +19 -7
- package/vendor/directive-core/dist/vbrief-reconcile/umbrellas.js.map +1 -1
package/Taskfile.yml
CHANGED
|
@@ -816,7 +816,9 @@ tasks:
|
|
|
816
816
|
cmds:
|
|
817
817
|
- task: engine:invoke
|
|
818
818
|
vars:
|
|
819
|
-
|
|
819
|
+
# #5805 / #5942: the pin's default project root is its own package dir, so pass the
|
|
820
|
+
# operator root explicitly (an operator --project-root in CLI_ARGS still wins).
|
|
821
|
+
ENGINE_CMD: 'release-publish --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
|
|
820
822
|
|
|
821
823
|
release:rollback:
|
|
822
824
|
desc: "State-aware release unwind (#716) -- task release:rollback -- <version> [--dry-run] [--allow-low-downloads N] [--allow-data-loss] [--force-strict-0]"
|
|
@@ -825,7 +827,9 @@ tasks:
|
|
|
825
827
|
cmds:
|
|
826
828
|
- task: engine:invoke
|
|
827
829
|
vars:
|
|
828
|
-
|
|
830
|
+
# #5805 / #5942: the pin's default project root is its own package dir, so pass the
|
|
831
|
+
# operator root explicitly (an operator --project-root in CLI_ARGS still wins).
|
|
832
|
+
ENGINE_CMD: 'release-rollback --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
|
|
829
833
|
|
|
830
834
|
release:e2e:
|
|
831
835
|
desc: "End-to-end release rehearsal against an auto-created temp repo (#716, #2572 keep+report default) -- task release:e2e [-- --dry-run] [--owner OWNER] [--destroy-repo]"
|
|
@@ -834,7 +838,9 @@ tasks:
|
|
|
834
838
|
cmds:
|
|
835
839
|
- task: engine:invoke
|
|
836
840
|
vars:
|
|
837
|
-
|
|
841
|
+
# #5805 / #5942: the pin's default project root is its own package dir, so pass the
|
|
842
|
+
# operator root explicitly (an operator --project-root in CLI_ARGS still wins).
|
|
843
|
+
ENGINE_CMD: 'release-e2e --project-root "{{.USER_WORKING_DIR}}" {{.CLI_ARGS}}'
|
|
838
844
|
|
|
839
845
|
release:wait-npm:
|
|
840
846
|
desc: "Phase 7 wait until both public OVPR packages (@deftai/directive + @deftai/directive-runtime) list the cut version (#4267 / #5490) -- task release:wait-npm -- <version>"
|
|
@@ -945,10 +951,10 @@ tasks:
|
|
|
945
951
|
;;
|
|
946
952
|
esac
|
|
947
953
|
print_deposit_recovery() {
|
|
948
|
-
echo "Phase 7 local closeout INCOMPLETE (#5953)." >&2
|
|
949
|
-
echo "Recovery:
|
|
954
|
+
echo "Phase 7 local closeout INCOMPLETE (#5953 / #6163 Prefer-A Bound 6772886751)." >&2
|
|
955
|
+
echo "Recovery: on the pin candidate, repair/write agent-hook deposit then force-add tip-required Cursor paths (.cursor/hooks.json + .cursor/hooks/deft-hook-<sha8>.mjs) so they co-travel with the pin (preparePinCandidateCursorDeposit / git add -f bounded set); merge that pin+deposit tip, then re-run:" >&2
|
|
950
956
|
echo " task release:local-update -- $VERSION --checkout $CHECKOUT" >&2
|
|
951
|
-
echo "Expected managed deposit paths include: .grok/hooks/ .cursor/hooks.json .claude/settings.json .codex/ (and related deposit files)." >&2
|
|
957
|
+
echo "Expected managed deposit paths include: .grok/hooks/ .cursor/hooks.json .cursor/hooks/deft-hook-<sha8>.mjs .claude/settings.json .codex/ (and related deposit files)." >&2
|
|
952
958
|
echo "Do not auto-commit deposit bytes inside the release flow; do not npm i -g." >&2
|
|
953
959
|
}
|
|
954
960
|
OCC_FILE="$CHECKOUT/.deft/occupancy.json"
|
|
@@ -1015,8 +1021,16 @@ tasks:
|
|
|
1015
1021
|
git status --short >&2
|
|
1016
1022
|
exit 1
|
|
1017
1023
|
fi
|
|
1018
|
-
|
|
1019
|
-
|
|
1024
|
+
# #6164: shell parameter expansion strip (zero sed/awk/cut/grep); then git verify.
|
|
1025
|
+
ORIGIN_HEAD="$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null || true)"
|
|
1026
|
+
DEFAULT_BRANCH="${ORIGIN_HEAD#refs/remotes/origin/}"
|
|
1027
|
+
if [ -z "$DEFAULT_BRANCH" ] || [ "$DEFAULT_BRANCH" = "$ORIGIN_HEAD" ]; then
|
|
1028
|
+
DEFAULT_BRANCH=master
|
|
1029
|
+
fi
|
|
1030
|
+
if ! git rev-parse --verify --quiet "refs/remotes/origin/$DEFAULT_BRANCH" >/dev/null; then
|
|
1031
|
+
echo "release:local-update: refuse; cannot verify refs/remotes/origin/$DEFAULT_BRANCH; run: git remote set-head origin -a" >&2
|
|
1032
|
+
exit 1
|
|
1033
|
+
fi
|
|
1020
1034
|
CURRENT="$(git rev-parse --abbrev-ref HEAD)"
|
|
1021
1035
|
if [ "$CURRENT" != "$DEFAULT_BRANCH" ]; then
|
|
1022
1036
|
echo "release:local-update: refuse; checkout must be on default branch $DEFAULT_BRANCH (on $CURRENT)" >&2
|
|
@@ -1024,14 +1038,54 @@ tasks:
|
|
|
1024
1038
|
fi
|
|
1025
1039
|
echo "release:local-update: ff-only pull on $DEFAULT_BRANCH at $CHECKOUT"
|
|
1026
1040
|
git pull --ff-only
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1041
|
+
# #6164: unconditional dependency-free AgeExclude gate (exact list-item; fail closed; no soft-skip).
|
|
1042
|
+
node -e '
|
|
1043
|
+
const fs = require("fs");
|
|
1044
|
+
const version = process.argv[1] || "";
|
|
1045
|
+
const want = "@deftai/directive-runtime@" + version;
|
|
1046
|
+
let text;
|
|
1047
|
+
try {
|
|
1048
|
+
text = fs.readFileSync("pnpm-workspace.yaml", "utf8");
|
|
1049
|
+
} catch (err) {
|
|
1050
|
+
const detail = err && err.message ? err.message : String(err);
|
|
1051
|
+
console.error("release:local-update: AgeExclude cannot read pnpm-workspace.yaml before frozen install (#6164): " + detail);
|
|
1052
|
+
process.exit(1);
|
|
1053
|
+
}
|
|
1054
|
+
const lines = text.split(/\r?\n/);
|
|
1055
|
+
const KEY_RE = /^(\s*)minimumReleaseAgeExclude:\s*(#.*)?$/;
|
|
1056
|
+
const ITEM_RE = /^(\s*)-\s+(.*)$/;
|
|
1057
|
+
const keyIdx = lines.findIndex((l) => KEY_RE.test(l));
|
|
1058
|
+
if (keyIdx < 0) process.exit(0);
|
|
1059
|
+
const keyLine = lines[keyIdx];
|
|
1060
|
+
const keyIndent = keyLine.length - keyLine.trimStart().length;
|
|
1061
|
+
const itemValue = (raw) => {
|
|
1062
|
+
let v = String(raw || "").trim();
|
|
1063
|
+
const SQ = String.fromCharCode(39);
|
|
1064
|
+
const DQ = String.fromCharCode(34);
|
|
1065
|
+
if (v.startsWith(SQ) || v.startsWith(DQ)) {
|
|
1066
|
+
const q = v[0];
|
|
1067
|
+
const end = v.indexOf(q, 1);
|
|
1068
|
+
return end > 0 ? v.slice(1, end) : v;
|
|
1069
|
+
}
|
|
1070
|
+
const hash = v.search(/\s#/);
|
|
1071
|
+
if (hash >= 0) v = v.slice(0, hash);
|
|
1072
|
+
return v.trim();
|
|
1073
|
+
};
|
|
1074
|
+
let found = false;
|
|
1075
|
+
for (let i = keyIdx + 1; i < lines.length; i++) {
|
|
1076
|
+
const line = lines[i];
|
|
1077
|
+
const trimmed = line.trim();
|
|
1078
|
+
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
1079
|
+
const m = ITEM_RE.exec(line);
|
|
1080
|
+
const indent = line.length - line.trimStart().length;
|
|
1081
|
+
if (m === null || indent < keyIndent) break;
|
|
1082
|
+
if (itemValue(m[2]) === want) { found = true; break; }
|
|
1083
|
+
}
|
|
1084
|
+
if (!found) {
|
|
1085
|
+
console.error("release:local-update: AgeExclude missing " + want + " before frozen install (#5953 footnote / #6164)");
|
|
1086
|
+
process.exit(1);
|
|
1087
|
+
}
|
|
1088
|
+
' "$VERSION"
|
|
1035
1089
|
echo "release:local-update: pnpm install --frozen-lockfile"
|
|
1036
1090
|
pnpm install --frozen-lockfile
|
|
1037
1091
|
PIN_PKG="$(node -e 'const fs=require("fs"); const p=JSON.parse(fs.readFileSync("package.json","utf8")); const v=(p.dependencies||{})["@deftai/directive-runtime"]||(p.devDependencies||{})["@deftai/directive-runtime"]||""; console.log(String(v).replace(/^[^0-9]*/, ""));')"
|
|
@@ -1051,6 +1105,39 @@ tasks:
|
|
|
1051
1105
|
print_deposit_recovery
|
|
1052
1106
|
exit 1
|
|
1053
1107
|
fi
|
|
1108
|
+
# #6163 Prefer-A Bound 6772886751: committed-byte HEAD association (no repair/staging).
|
|
1109
|
+
echo "release:local-update: require committed Cursor deposit association on HEAD"
|
|
1110
|
+
if ! node --input-type=module -e '
|
|
1111
|
+
import { pathToFileURL } from "node:url";
|
|
1112
|
+
import { join, resolve } from "node:path";
|
|
1113
|
+
const checkout = process.argv[1];
|
|
1114
|
+
const vendor = join(
|
|
1115
|
+
checkout,
|
|
1116
|
+
"node_modules/@deftai/directive-runtime/vendor/directive-core/dist/init-deposit/agent-hooks.js",
|
|
1117
|
+
);
|
|
1118
|
+
let validate;
|
|
1119
|
+
try {
|
|
1120
|
+
({ validateCursorDepositAssociation: validate } = await import(pathToFileURL(resolve(vendor)).href));
|
|
1121
|
+
} catch (err) {
|
|
1122
|
+
const detail = err && err.message ? err.message : String(err);
|
|
1123
|
+
console.error("release:local-update: cannot load validateCursorDepositAssociation from installed runtime (#6163): " + detail);
|
|
1124
|
+
process.exit(1);
|
|
1125
|
+
}
|
|
1126
|
+
if (typeof validate !== "function") {
|
|
1127
|
+
console.error("release:local-update: installed runtime lacks validateCursorDepositAssociation (#6163); pin must co-travel the Bound helper");
|
|
1128
|
+
process.exit(1);
|
|
1129
|
+
}
|
|
1130
|
+
const result = validate(checkout, { source: "HEAD" });
|
|
1131
|
+
if (!result || result.ok !== true) {
|
|
1132
|
+
console.error("release:local-update: committed Cursor deposit association FAILED on HEAD (#6163)");
|
|
1133
|
+
console.error(result && result.detail ? result.detail : "unknown association failure");
|
|
1134
|
+
process.exit(1);
|
|
1135
|
+
}
|
|
1136
|
+
console.log("release:local-update: committed Cursor deposit association ok (" + result.sha8 + ")");
|
|
1137
|
+
' "$CHECKOUT"; then
|
|
1138
|
+
print_deposit_recovery
|
|
1139
|
+
exit 1
|
|
1140
|
+
fi
|
|
1054
1141
|
UPGRADE_BIN=""
|
|
1055
1142
|
if [ -x "node_modules/.bin/directive" ]; then
|
|
1056
1143
|
UPGRADE_BIN="node_modules/.bin/directive"
|
package/commands.md
CHANGED
|
@@ -149,6 +149,8 @@ Common commands:
|
|
|
149
149
|
- Bare `evidence` / `disposition` keys are **not** valid (#1620 / Option B #3305). They fail `verify:vbrief-conformance` and are treated as missing by `scope:complete` (no dual-read). Migrate bare keys or narrative-only `Result`/`Verification` workarounds to the namespaced fields above while items are still non-terminal.
|
|
150
150
|
- Already-terminal items (`completed`/`failed`/`cancelled`/…) are not re-checked for typed evidence; pre-marking items complete to skip the gate is unsupported for the typed path.
|
|
151
151
|
- `merge`/`review` alone cannot satisfy smoke/UAT/deploy/observed_behavior criteria (title/Acceptance text or explicit axis).
|
|
152
|
+
- **Delivery identity (#5710):** with an issue origin, the PR must be associated with the issue as of `merged_at` by one of: (a) a unique sole `deft-story: N` mark in the merge-time PR body (PRs that change only lifecycle briefs, `CHANGELOG.md` and the spec-drift ledger are excluded and never count), (b) a closing keyword (`Closes #N`, `owner/repo#N`, or the issue URL) in the merge-time body, or (c) a Connected issue link at `merged_at`. Brief-in-PR-diff still counts. A mark, keyword or link added after merge does not count, and current `closingIssuesReferences` alone is not enough. Basis (a) scans every merged PR within 20 minutes, `graphql.remaining` ≥ 200 and REST core ≥ 100; any gap refuses. With no issue origin, stamp `plan.metadata.productPullRequest` and complete with `--pr <that number> --merge-commit <sha>`; the repository is the `origin` remote. `--non-delivery` refuses when the stamped PR is merged, and refuses when its merge state cannot be read. `xbrief:preflight` prints one note for a code-bearing brief with no issue origin.
|
|
153
|
+
- **Lifecycle completion (#680):** completion admission uses the merge-time bases above; there is no separate keyword-only refuse. `swarm:finalize-owed` also lists an active issue-less brief with a stamped `productPullRequest` (shown as `pr #P`, no issue number, no origin-issue check or origin-close); briefs that share a product PR finalize together under one claim. In a batched `swarm:finalize-cohort`, a refused `deft-story` mark is not an error: that story stays pending, unstamped and unswept (`refused_marks`, exit 3 incomplete), and the other stories stamp, sweep, commit and land. A failed changed-file lookup and real delivery errors still fail the batch. `scope:complete` writes the spec-drift ledger as verb output; finalize commits it with the completed briefs, and scope-provenance membership admits exactly `xbrief/.audit/spec-drift-ledger.json` only when the whole change set is a completion move. `verify:changelog-unreleased` needs no new `[Unreleased]` line when the change set versus merge-base (`git diff --name-only --no-renames`) is non-empty and only lifecycle briefs, `CHANGELOG.md` and that ledger, so lifecycle verbs write no CHANGELOG line. Product changes and an empty change set still need one.
|
|
152
154
|
- `task scope:fail -- xbrief/active/<file>.xbrief.json` -- mark running work failed when the scope cannot complete.
|
|
153
155
|
- `task scope:cancel -- <path>` -- move a scope to `cancelled/`.
|
|
154
156
|
- `task scope:restore`, `task scope:block`, `task scope:unblock`, `task scope:demote`, and `task scope:undo:*` -- repair or reverse lifecycle transitions.
|
|
@@ -181,6 +183,8 @@ The implementation gate succeeds only for active scope xBRIEFs with `plan.status
|
|
|
181
183
|
|
|
182
184
|
**Hotfix classifier (#1193):** Typed `plan.policy.hotfixCriteria` + pure `evaluateHotfixEligibility`. Small fix / pure revert may propose label `hotfix-candidate` only; a human promotes to `hotfix`. Refactors, new exports/handlers, and forbidden paths (Dockerfile, fly.toml, workflows, migrations, auth/secrets) never qualify.
|
|
183
185
|
|
|
186
|
+
**Small-change lane (#6204):** Under the `rapid` ceremony dial, a scope with stated acceptance commands may use a thin brief: title, issue ref, `file_scope` and acceptance commands only (`emitThinBrief`, `packages/core/src/intake/thin-brief.ts`). The bound defaults to 5 production files and 500 changed lines (added plus deleted, tests included; briefs and CHANGELOG excluded). `plan.policy.ceremonyDial.smallChangeLane` (`maxProductionFiles`, `maxChangedLines`) overrides each field; an invalid value refuses. `scope:promote`, `scope:activate`, `xbrief:preflight`, `verify:ac` and `scope:complete` admit the thin shape, and each one remeasures the cumulative product diff (git, against the lane-admit base in `plan.metadata["x-directive/smallChangeLane"]`; at `scope:complete`, the delivered merge commit against its first parent). A crossed bound, a public-surface change (manifest, entry, task file or export line), an unknown public surface, or a depth other than `rapid` refuses. A crossed bound or public-surface change is recorded on the brief (lane state `crossed`), so every later retry refuses even if the diff shrinks. The public-surface check reads export statements with the TypeScript parser, so a change inside a multi-line export list or exported signature counts. The one remediation is lane exit: convert the scope to the full shape (`escalateThinBriefToFullShape`), which keeps scope identity and acceptance evidence. Raising the dial (#3319) does not clear the refusal. One-PR delivery is not part of this lane; Path B brief-first stays lawful (#6203 / #680).
|
|
187
|
+
|
|
184
188
|
```mermaid
|
|
185
189
|
flowchart TD
|
|
186
190
|
Candidate["Scope xBRIEF exists"] --> Promote{"In proposed?"}
|
|
@@ -494,13 +498,14 @@ Cross-link: spawn three postures and deny recoveries live under § Agent-host di
|
|
|
494
498
|
### Mutable ritual (mutation posture)
|
|
495
499
|
|
|
496
500
|
- ! On **mutation** session start, run `deft session:start` (or `task session:start` in framework source) after loading AGENTS.md. Records quick-tier ritual in `.deft/ritual-state.json`: alignment confirmation, branch-policy disclosure, `deft verify:tools` guidance, default-branch sync warnings, and `deft triage:welcome` one-liner. State is worktree- and HEAD-bound; stale after `plan.policy.sessionRitualStalenessHours` hours (default 8). Mutation start also claims the worktree occupancy lease (`.deft/occupancy.json`); see Session routing (#3433).
|
|
497
|
-
- ! **
|
|
501
|
+
- ! **Quiet-default success stdout (#4966):** mutation `session:start` success human output defaults to one trusted go-signal line with stable prefix `[deft session] ready=…` (ready + branch/HEAD + occupancy). Routine dial/host-surface/effort/orientation/ceremony prose is verbose-only via `deft session:start --verbose` or `DEFT_SESSION_VERBOSE=1`. Non-exhaustive always-on allowlist keeps actionable zero-exit exceptions, non-default policy/bypass and human-merge disclosures, finalize-owed unknown/deferred qualifications, and actionable lifecycle/work-claim notices on the quiet path. Fail/deny/steal/non-zero stay loud. Tip `#3286` `--compact` / `DEFT_SESSION_COMPACT` are deprecated as whole-stdout volume controls (no-op for human stdout selection; `--json` / run-summary unchanged). Exit 0 is not the mutation go-signal — render from structured ready/posture/occupancy state.
|
|
502
|
+
- ! **Orientation compression Now (#3286):** mutation cold `session:start` composes `doctor` + #3282 toolchain preflight (and deposit-sha fast-paths for `agents:refresh` / `verify:cache-fresh`) as inline sections with per-section status lines — composition of existing steps, not a new monolith. When the deposit fingerprint (payload + templates + engine) is unchanged, refresh surfaces print one-line `unchanged - sha match` no-ops. Section formatting may still honor `--compact` / `DEFT_SESSION_COMPACT=1` inside the orientation bundle; whole-stdout volume is `#4966` quiet-default / `--verbose`. #2176 read-only default is unchanged. Dual-path Later (orient) stays open until run-summary telemetry shows ritual+gate share ≥ 25% after Now ships (#2899).
|
|
498
503
|
- ! Cold `session:start` does **not** run the live agent-hook probe. Functional readiness belongs to the gated mutation path so cold ceremony retains the #2990/#2991 latency profile.
|
|
499
504
|
- ! **Hot path latency (#2991):** by default, mutation `session:start` does **not** block ritual-state write on optional network. It skips the npm release-availability probe and triage cache empty-hydrate / self-heal (`ensureTriageCacheHydrated` / `maybeSelfHealCache`). Targets (operator-facing, not CI-hard): warm hot path typically under a few seconds once tools are on PATH; cold path dominated by local `verify:tools` and git, usually well under ~30s when optional network is off. Empty-cache GitHub fetch-all and npm `view` previously accounted for multi-minute hangs in the WWYSYDH pilot — those stay off the critical path unless opted in.
|
|
500
505
|
- ! **Cold vs re-arm ceremony tiers (#2992):** default `session:start` is the **cold** (full) path. After age staleness or compact re-arm (#2113) on the **same worktree** with continuous HEAD and previously-passing quick steps, prefer `deft session:start --rearm` (alias `--tier=rearm`) to refresh the ritual clock + HEAD/worktree bind without `verify:tools`, triage welcome, release probe, or staleness tickler. Full cold remains required for missing/invalid state, worktree change, discontinuous HEAD, first install, or failed/missing quick steps. Compact marks `rearm_needed`; PreToolUse denial and inspect/verify messages prefer re-arm recovery when cold is unnecessary.
|
|
501
506
|
- ? Opt into optional network: `deft session:start --with-network` or `DEFT_SESSION_START_NETWORK=1`. When enabled, the bounded release-availability probe runs against the public npm registry (skips framework source checkouts, non-release pins, and `DEFT_NO_NETWORK=1`; identical latest-version notices throttle for 24 hours in `xbrief/.triage-cache/release-availability-state.json`). Default-mode triage welcome then also hydrates/self-heals the triage cache. This is separate from `deft doctor`, whose bare and gated invocations remain offline by default (#2182). Refs #1692 / #2991. Re-arm never runs optional network.
|
|
502
507
|
- ~ `session:start --json` includes `steps[]` with `name` + `duration_ms` for major phases (`alignment`, `branch_policy`, `verify_tools`, `triage_welcome`, `release_probe`, `ritual_write`), plus total `duration_ms`, `optional_network`, and `ceremony_tier` (`cold` | `rearm`). Skipped optional steps report `skipped: true` and `duration_ms: 0`. Use this for attribution when investigating ceremony wall-clock.
|
|
503
|
-
- ~ **Process-cost events (#2994 / #3508):** on mutation `session:start` completion (cold or re-arm), Directive appends a local `session:start` behavioral event to `.deft-cache/events.jsonl` with `ceremony_tier`, `duration_ms`, `exit_code`, and optional `steps[]` (same labels as `--json`). Mutation `session:start` also
|
|
508
|
+
- ~ **Process-cost events (#2994 / #3508):** on mutation `session:start` completion (cold or re-arm), Directive appends a local `session:start` behavioral event to `.deft-cache/events.jsonl` with `ceremony_tier`, `duration_ms`, `exit_code`, and optional `steps[]` (same labels as `--json`). Mutation `session:start` also emits one operator-visible `ceremony <tier> <ms>` line on the verbose human path (`--verbose` / `DEFT_SESSION_VERBOSE`; quiet-default hides it per #4966). When PreToolUse denies for `ritual-not-ready`, a local `session:ritual-blocked` event records `tool_name`, `code`, and optional `recovery_tier` / `detail`. Always-on best-effort (never blocks ceremony or deny path); not gated on `valueFeedback`; **no remote upload** (Product Insights #2603 is a separate opt-in). Pull the rollup with `task value:show` (composed reader; CLI process time, not agent-turn wall clock). See § Process-cost events below. ⊗ Do not use the printed CLI duration as #3286 Later graduation input.
|
|
504
509
|
- ~ At safe idle points (clean tree, no in-flight story), mutation session start and `deft scope:complete` may also run the staleness tickler: an interactive, consent-based offer to upgrade Directive (`npm i -g @deftai/directive@latest`) and/or migrate xBRIEF (`deft migrate:xbrief`). Escalation tiers, snooze windows, and opt-out live under `plan.policy.stalenessTickler` — inspect with `deft policy:show --field=stalenessTickler`. State persists in `xbrief/.triage-cache/staleness-tickler-state.json`. Skips framework source checkouts, dirty trees, CI/headless (`DEFT_SESSION_RITUAL_SKIP=1`), and typed opt-out. Refs #2488 / #2489.
|
|
505
510
|
- ! Before any code-writing tool call or `start_agent` implementation dispatch, run `deft verify:session-ritual -- --tier=gated`. Gated tier fails closed unless quick-tier state is fresh; lazily records the non-deferrable `agent_hooks` readiness gate plus `deft doctor` and `task verify:cache-fresh` entrypoints. Agent-hook correctness is independent of doctor warnings and throttling. Step 0 of the pre-`start_agent` gate stack.
|
|
506
511
|
- ! **One-shot recovery (#2993 / #3100 / #4399):** when PreToolUse denies writes for a stale/missing gated ritual, run `deft session:ready` (or `task session:ready`). It composes `session:start` (only when quick-tier is not green) + `verify:session-ritual -- --tier=gated` with check-class `cache_fresh` (age + live drift, no `--skip-drift-probe`) + `cache fetch-all --force` when `cache_fresh` is the remaining blocker, then re-verifies. Skip-drift ritual argv is not the ready bar — that is the `task check` / FRAMEWORK_CHECK_GATES evaluate, so stale-by-drift can still fire fetch-all. Even when gated inspect is already fresh, the fast path forces one live `agent_hooks` check and the same check-class `cache_fresh`; it still avoids unnecessary fetch-all when that evaluate is green. Flags: `--json`, `--repo OWNER/NAME`, `--with-network` (forwarded to session:start). Prefer this over juggling the multi-step recovery sequence under hook pressure.
|
|
@@ -550,7 +555,7 @@ Local ceremony cost signal for WWYSYDH / weekly process rollups. Emits to `.deft
|
|
|
550
555
|
| PreToolUse ritual deny | `session:ritual-blocked` | Hook blocks write/spawn because gated ritual is not ready | `tool_name`, `code=ritual-not-ready`, `recovery_tier` (`cold`\|`rearm`) |
|
|
551
556
|
| Per-step wall-clock | (field on `session:start`) | Same emit as session start | `steps[].name` + `steps[].duration_ms` (`alignment`, `scm_readiness`, `branch_policy`, `verify_tools`, `triage_welcome`, `release_probe`, `ritual_write`) |
|
|
552
557
|
|
|
553
|
-
CLI mirror (no JSONL required): `deft session:start --json` already exposes the same `steps` / `duration_ms` / `ceremony_tier` fields for one-shot inspection. Human mutation output adds one `ceremony <tier> <ms>` line
|
|
558
|
+
CLI mirror (no JSONL required): `deft session:start --json` already exposes the same `steps` / `duration_ms` / `ceremony_tier` fields for one-shot inspection. Human mutation output adds one `ceremony <tier> <ms>` line on the verbose path (#3508 / #4966).
|
|
554
559
|
|
|
555
560
|
Composed reader (no new verb): `task value:show` / `deft value:show` prints last cold vs re-arm duration, last per-step breakdown, blocked-ritual count, and recovery-tier distribution from the same ledger. This is **CLI process time**, not agent-turn wall clock (#3500). ⊗ Do not feed the printed CLI duration into #3286 Later graduation.
|
|
556
561
|
|
package/dist/session-start.d.ts
CHANGED
|
@@ -30,10 +30,16 @@ export interface ParsedSessionStartArgs {
|
|
|
30
30
|
hardBudget?: boolean;
|
|
31
31
|
};
|
|
32
32
|
/**
|
|
33
|
-
* #3286: compact
|
|
34
|
-
*
|
|
33
|
+
* #3286 / #4966: tip `--compact` retained for argv compatibility; deprecated
|
|
34
|
+
* as whole-stdout volume gate (no-op for human stdout selection). Orientation
|
|
35
|
+
* section formatting may still honor it inside core.
|
|
35
36
|
*/
|
|
36
37
|
compact: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* #4966: restore full ceremony human stdout. Quiet success is the default.
|
|
40
|
+
* Also settable via DEFT_SESSION_VERBOSE=1.
|
|
41
|
+
*/
|
|
42
|
+
verbose: boolean;
|
|
37
43
|
/** #3433: steal the worktree occupancy lease. */
|
|
38
44
|
steal: boolean;
|
|
39
45
|
confirm: boolean;
|
package/dist/session-start.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { CEREMONY_DEPTHS, normalizeCeremonyModelTier, normalizeCeremonyProjectShape, normalizeCeremonyTaskSize, selectCeremonyDepth, } from "../vendor/directive-core/dist/policy/index.js";
|
|
5
|
-
import { COLD_CEREMONY_TIER, PRIMARY_CLAIM_EXCEPTIONS, parseDeferrals, parseSessionPostureToken, publishApplyingHostFromCanonicalSessionId, READ_ONLY_POSTURE, REARM_CEREMONY_TIER, REQUIREMENTS_POSTURE, resolveProductionHostEffortDescriptor, ritualStatePath, runSessionStart, SESSION_CEREMONY_TIERS, } from "../vendor/directive-core/dist/session/index.js";
|
|
5
|
+
import { COLD_CEREMONY_TIER, PRIMARY_CLAIM_EXCEPTIONS, parseDeferrals, parseSessionPostureToken, publishApplyingHostFromCanonicalSessionId, READ_ONLY_POSTURE, REARM_CEREMONY_TIER, REQUIREMENTS_POSTURE, resolveProductionHostEffortDescriptor, resolveSessionVerbose, ritualStatePath, runSessionStart, SESSION_CEREMONY_TIERS, selectSessionStartHumanLines, } from "../vendor/directive-core/dist/session/index.js";
|
|
6
6
|
function parseCeremonyDepth(raw) {
|
|
7
7
|
const value = raw.trim().toLowerCase();
|
|
8
8
|
if (CEREMONY_DEPTHS.includes(value)) {
|
|
@@ -40,6 +40,7 @@ export function parseArgs(argv) {
|
|
|
40
40
|
ceremonyDepthOverride: null,
|
|
41
41
|
effortBudgetHost: {},
|
|
42
42
|
compact: false,
|
|
43
|
+
verbose: false,
|
|
43
44
|
steal: false,
|
|
44
45
|
confirm: false,
|
|
45
46
|
occupant: null,
|
|
@@ -311,9 +312,14 @@ export function parseArgs(argv) {
|
|
|
311
312
|
parsed.effortBudgetHost.hardBudget = true;
|
|
312
313
|
}
|
|
313
314
|
else if (arg === "--compact") {
|
|
314
|
-
// #3286:
|
|
315
|
+
// #3286 / #4966: deprecated as whole-stdout volume gate (parsed; no-op for
|
|
316
|
+
// human stdout selection). Prefer --verbose / DEFT_SESSION_VERBOSE.
|
|
315
317
|
parsed.compact = true;
|
|
316
318
|
}
|
|
319
|
+
else if (arg === "--verbose") {
|
|
320
|
+
// #4966: restore full ceremony dump (quiet success is the default).
|
|
321
|
+
parsed.verbose = true;
|
|
322
|
+
}
|
|
317
323
|
else if (arg === "--steal") {
|
|
318
324
|
parsed.steal = true;
|
|
319
325
|
}
|
|
@@ -496,8 +502,10 @@ export function run(argv) {
|
|
|
496
502
|
ceremonyTier: args.ceremonyTier,
|
|
497
503
|
ceremonyDialInputs: args.ceremonyDialInputs,
|
|
498
504
|
effortBudgetSeams: { hostDescriptor, environ: process.env },
|
|
499
|
-
// #3286: --compact
|
|
505
|
+
// #3286 / #4966: --compact retained for orientation formatting; not volume.
|
|
500
506
|
compact: args.compact ? true : undefined,
|
|
507
|
+
// #4966: --verbose / DEFT_SESSION_VERBOSE restores ceremony dump.
|
|
508
|
+
verbose: args.verbose ? true : undefined,
|
|
501
509
|
steal: args.steal ? true : undefined,
|
|
502
510
|
confirm: args.confirm ? true : undefined,
|
|
503
511
|
occupant: args.occupant ?? undefined,
|
|
@@ -586,27 +594,40 @@ export function run(argv) {
|
|
|
586
594
|
process.stdout.write(`${JSON.stringify(sorted)}\n`);
|
|
587
595
|
return result.code;
|
|
588
596
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
sink.write(`${line}\n`);
|
|
592
|
-
}
|
|
597
|
+
// #4966: one final human-output selection point (core lines + stray + trailer).
|
|
598
|
+
const trailer = [];
|
|
593
599
|
if (result.code === 0) {
|
|
594
600
|
const posture = result.payload.posture;
|
|
595
601
|
if (posture === READ_ONLY_POSTURE || posture === REQUIREMENTS_POSTURE) {
|
|
596
|
-
|
|
602
|
+
trailer.push(`[deft] ${String(result.payload.message)}`);
|
|
597
603
|
}
|
|
598
604
|
else {
|
|
599
|
-
|
|
605
|
+
trailer.push(`[deft] session ritual recorded at ${ritualStatePath(projectRoot)} (diagnostic-only; not posture authority)`);
|
|
600
606
|
}
|
|
601
607
|
if (applyingHostPublish.status === "deferred") {
|
|
602
|
-
|
|
608
|
+
trailer.push(`[deft] applying-host publish deferred: live occupant ${applyingHostPublish.liveSessionId} ` +
|
|
603
609
|
`still holds this worktree (attempted ${applyingHostPublish.attemptedSessionId}); ` +
|
|
604
|
-
"not a conflict recovery — that owner must release/end or wait for lease TTL
|
|
610
|
+
"not a conflict recovery — that owner must release/end or wait for lease TTL.");
|
|
605
611
|
}
|
|
606
612
|
else if (applyingHostPublish.status === "failed") {
|
|
607
|
-
|
|
613
|
+
trailer.push(`[deft] applying-host publish failed: ${applyingHostPublish.message}`);
|
|
608
614
|
}
|
|
609
615
|
}
|
|
616
|
+
const verbose = resolveSessionVerbose({
|
|
617
|
+
verbose: args.verbose ? true : undefined,
|
|
618
|
+
env: process.env,
|
|
619
|
+
});
|
|
620
|
+
const humanLines = selectSessionStartHumanLines({
|
|
621
|
+
code: result.code,
|
|
622
|
+
payload: payloadWithPublish,
|
|
623
|
+
lines: [...lines, ...trailer],
|
|
624
|
+
verbose,
|
|
625
|
+
env: process.env,
|
|
626
|
+
});
|
|
627
|
+
const sink = result.code === 0 ? process.stdout : process.stderr;
|
|
628
|
+
for (const line of humanLines) {
|
|
629
|
+
sink.write(`${line}\n`);
|
|
630
|
+
}
|
|
610
631
|
return result.code;
|
|
611
632
|
}
|
|
612
633
|
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/** One remediation line for a code-bearing brief with no issue origin (#5710 P2). */
|
|
3
|
+
export declare const ISSUELESS_CODE_BEARING_REMEDIATION: string;
|
|
4
|
+
/** The P2 remediation line when the brief is code-bearing with no issue origin, else null. */
|
|
5
|
+
export declare function issuelessCodeBearingNote(briefPath: string): string | null;
|
|
2
6
|
interface ParsedArgs {
|
|
3
7
|
vbriefPath: string | null;
|
|
4
8
|
emitJson: boolean;
|
package/dist/vbrief-preflight.js
CHANGED
|
@@ -1,9 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
2
3
|
import { dirname, resolve } from "node:path";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
4
5
|
import { emitJson, evaluate, PREFLIGHT_USAGE_HINT } from "../vendor/directive-core/dist/preflight/index.js";
|
|
5
6
|
import { scanWorkClaimForBriefPath } from "../vendor/directive-core/dist/scm/index.js";
|
|
7
|
+
import { isCodeBearingScope, resolvePlanGithubIssueRef } from "../vendor/directive-core/dist/scope/index.js";
|
|
6
8
|
import { refuseIfWslOwnershipBlocked } from "./wsl-ownership-guard.js";
|
|
9
|
+
/** One remediation line for a code-bearing brief with no issue origin (#5710 P2). */
|
|
10
|
+
export const ISSUELESS_CODE_BEARING_REMEDIATION = "[deft preflight] note: code-bearing brief has no x-xbrief/github-issue origin (#5710). " +
|
|
11
|
+
"PR-only complete: stamp plan.metadata.productPullRequest, then " +
|
|
12
|
+
"scope:complete -- --pr <that PR> --merge-commit <sha>. " +
|
|
13
|
+
"Issue-first: add an x-xbrief/github-issue reference before the product PR.";
|
|
14
|
+
/** The P2 remediation line when the brief is code-bearing with no issue origin, else null. */
|
|
15
|
+
export function issuelessCodeBearingNote(briefPath) {
|
|
16
|
+
let plan;
|
|
17
|
+
try {
|
|
18
|
+
const raw = JSON.parse(readFileSync(briefPath, "utf8"));
|
|
19
|
+
plan =
|
|
20
|
+
raw !== null && typeof raw === "object" && !Array.isArray(raw)
|
|
21
|
+
? raw.plan
|
|
22
|
+
: null;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
if (plan === null || typeof plan !== "object" || Array.isArray(plan)) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const record = plan;
|
|
31
|
+
if (!isCodeBearingScope(record) || resolvePlanGithubIssueRef(record) !== null) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return ISSUELESS_CODE_BEARING_REMEDIATION;
|
|
35
|
+
}
|
|
7
36
|
const HELP_TEXT = `usage: xbrief:preflight [--vbrief-path PATH | PATH] [--json] [--help]
|
|
8
37
|
|
|
9
38
|
Implementation-intent gate (#810): exits 0 only when the xBRIEF is in
|
|
@@ -117,6 +146,11 @@ export function run(argv, scan = scanWorkClaimForBriefPath) {
|
|
|
117
146
|
`[deft work-claim] warning: scan failed (${message}). Warn is success; this is not a GitHub lock.`,
|
|
118
147
|
];
|
|
119
148
|
}
|
|
149
|
+
// #5710 P2: advisory only; the exit code is unchanged.
|
|
150
|
+
const issuelessNote = issuelessCodeBearingNote(vbriefPath);
|
|
151
|
+
if (issuelessNote !== null) {
|
|
152
|
+
scanLines = [issuelessNote, ...scanLines];
|
|
153
|
+
}
|
|
120
154
|
if (args.emitJson) {
|
|
121
155
|
const message = scanLines.length > 0 ? `${result.message}\n${scanLines.join("\n")}` : result.message;
|
|
122
156
|
process.stdout.write(`${emitJson(vbriefPath, result.exitCode, message)}\n`);
|
|
@@ -8,9 +8,21 @@ interface ParsedArgs {
|
|
|
8
8
|
/** Raw `--enforce` token present, even when a later `--warn` wins `enforce`. */
|
|
9
9
|
enforcePresent: boolean;
|
|
10
10
|
quiet: boolean;
|
|
11
|
+
/** PR number for the forge-approval admit (#6203); null when absent. */
|
|
12
|
+
prNumber: number | null;
|
|
13
|
+
/** OWNER/NAME for the forge-approval admit; empty means resolve. */
|
|
14
|
+
repo: string;
|
|
11
15
|
error?: string;
|
|
12
16
|
}
|
|
13
17
|
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
14
|
-
|
|
18
|
+
/**
|
|
19
|
+
* PR context for the forge-approval admit (#6203): flags win, then
|
|
20
|
+
* GITHUB_PR_NUMBER (digits only) / GITHUB_REPOSITORY. Null without a PR.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveForgeContext(args: Pick<ParsedArgs, "prNumber" | "repo">, env: NodeJS.ProcessEnv): {
|
|
23
|
+
prNumber: number;
|
|
24
|
+
repo: string | null;
|
|
25
|
+
} | null;
|
|
26
|
+
export declare function run(argv: string[], env?: NodeJS.ProcessEnv): number;
|
|
15
27
|
export {};
|
|
16
28
|
//# sourceMappingURL=verify-scope-provenance.d.ts.map
|
|
@@ -6,12 +6,21 @@
|
|
|
6
6
|
* evaluated inside evaluateScopeProvenance. This CLI forwards --base-ref.
|
|
7
7
|
* `--enforce` / `--warn` stay parsed for compatibility; `--enforce` is a
|
|
8
8
|
* no-op after #4956 / #5002 and prints a stderr notice when present.
|
|
9
|
+
*
|
|
10
|
+
* `--pr N` / `--repo OWNER/NAME` (env fallback GITHUB_PR_NUMBER /
|
|
11
|
+
* GITHUB_REPOSITORY) supply PR context for the forge-approved head-brief
|
|
12
|
+
* membership admit (#6203). Without a PR number the admit is never consulted.
|
|
9
13
|
*/
|
|
10
14
|
import { resolve } from "node:path";
|
|
11
15
|
import { scopeProvenance } from "../vendor/directive-core/dist/index.js";
|
|
12
16
|
import { isDirectEntrypoint } from "./entrypoint.js";
|
|
13
17
|
/** Stderr line when argv includes `--enforce`. Exit code is unchanged. */
|
|
14
18
|
export const ENFORCE_NOOP_NOTICE = "verify_scope_provenance: --enforce has no effect after #4956 / #5002";
|
|
19
|
+
const PR_DIGITS = /^[1-9][0-9]*$/;
|
|
20
|
+
function parsePr(value) {
|
|
21
|
+
const t = value.trim();
|
|
22
|
+
return PR_DIGITS.test(t) ? Number(t) : null;
|
|
23
|
+
}
|
|
15
24
|
export function parseArgs(argv) {
|
|
16
25
|
const parsed = {
|
|
17
26
|
projectRoot: ".",
|
|
@@ -20,6 +29,8 @@ export function parseArgs(argv) {
|
|
|
20
29
|
enforce: false,
|
|
21
30
|
enforcePresent: false,
|
|
22
31
|
quiet: false,
|
|
32
|
+
prNumber: null,
|
|
33
|
+
repo: "",
|
|
23
34
|
};
|
|
24
35
|
for (let i = 0; i < argv.length; i += 1) {
|
|
25
36
|
const arg = argv[i];
|
|
@@ -55,22 +66,54 @@ export function parseArgs(argv) {
|
|
|
55
66
|
else if (arg?.startsWith("--base-ref=")) {
|
|
56
67
|
parsed.baseRef = arg.slice("--base-ref=".length);
|
|
57
68
|
}
|
|
69
|
+
else if (arg === "--pr" || arg?.startsWith("--pr=")) {
|
|
70
|
+
const value = arg === "--pr" ? argv[i + 1] : arg.slice("--pr=".length);
|
|
71
|
+
if (arg === "--pr")
|
|
72
|
+
i += 1;
|
|
73
|
+
const pr = value === undefined ? null : parsePr(value);
|
|
74
|
+
if (pr === null) {
|
|
75
|
+
return { ...parsed, error: "argument --pr: expected a positive PR number" };
|
|
76
|
+
}
|
|
77
|
+
parsed.prNumber = pr;
|
|
78
|
+
}
|
|
79
|
+
else if (arg === "--repo" || arg?.startsWith("--repo=")) {
|
|
80
|
+
const value = arg === "--repo" ? argv[i + 1] : arg.slice("--repo=".length);
|
|
81
|
+
if (arg === "--repo")
|
|
82
|
+
i += 1;
|
|
83
|
+
if (value === undefined || value.trim().length === 0) {
|
|
84
|
+
return { ...parsed, error: "argument --repo: expected OWNER/NAME" };
|
|
85
|
+
}
|
|
86
|
+
parsed.repo = value.trim();
|
|
87
|
+
}
|
|
58
88
|
else {
|
|
59
89
|
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
60
90
|
}
|
|
61
91
|
}
|
|
62
92
|
return parsed;
|
|
63
93
|
}
|
|
64
|
-
|
|
94
|
+
/**
|
|
95
|
+
* PR context for the forge-approval admit (#6203): flags win, then
|
|
96
|
+
* GITHUB_PR_NUMBER (digits only) / GITHUB_REPOSITORY. Null without a PR.
|
|
97
|
+
*/
|
|
98
|
+
export function resolveForgeContext(args, env) {
|
|
99
|
+
const prNumber = args.prNumber ?? parsePr(env.GITHUB_PR_NUMBER ?? "");
|
|
100
|
+
if (prNumber === null)
|
|
101
|
+
return null;
|
|
102
|
+
const repo = args.repo.length > 0 ? args.repo : (env.GITHUB_REPOSITORY ?? "").trim();
|
|
103
|
+
return { prNumber, repo: repo.length > 0 ? repo : null };
|
|
104
|
+
}
|
|
105
|
+
export function run(argv, env = process.env) {
|
|
65
106
|
const args = parseArgs(argv);
|
|
66
107
|
if (args.error !== undefined) {
|
|
67
108
|
process.stderr.write(`verify_scope_provenance: ${args.error}\n`);
|
|
68
109
|
return 2;
|
|
69
110
|
}
|
|
70
111
|
const projectRoot = resolve(args.projectRoot);
|
|
112
|
+
const forgeApproval = resolveForgeContext(args, env);
|
|
71
113
|
const result = scopeProvenance.evaluateScopeProvenance(projectRoot, {
|
|
72
114
|
...(args.baseRef.length > 0 ? { baseRef: args.baseRef } : {}),
|
|
73
115
|
enforce: args.enforce,
|
|
116
|
+
...(forgeApproval !== null ? { forgeApproval } : {}),
|
|
74
117
|
});
|
|
75
118
|
if (args.enforcePresent) {
|
|
76
119
|
process.stderr.write(`${ENFORCE_NOOP_NOTICE}\n`);
|
package/docs/getting-started.md
CHANGED
|
@@ -58,6 +58,8 @@ directive doctor
|
|
|
58
58
|
|
|
59
59
|
After clone or a new worktree: run the project package manager (`npm install` / `pnpm install`), then the first eligible runtime command or registered hook completes local activation.
|
|
60
60
|
|
|
61
|
+
**Dependabot / pin-only bumps (AC14.10):** a PR that changes only the `@deftai/directive-runtime` pin + lockfile is valid. After `pnpm install` / `npm install` brings `node_modules` to the new pin, the first eligible skill, hook, or ordinary runtime command runs local automatic setup (wiring / `GENERATION` / readiness). `directive doctor` stays read-only — dirty doctor output before that eligible invocation is expected. CI may either rely on that first eligible command or run explicit `directive install --repair --yes`. Do not treat "deposit bytes must land in the same pin PR" as product law. Human-test mirror: HT-17 in `docs/installer-refactor-ovpr/install-ovpr-human-test-plan.md`. Schema/data behind the new pin still needs `directive upgrade` (AC14.11), not repair alone.
|
|
62
|
+
|
|
61
63
|
**Repair (pin unchanged):** `npm i -g @deftai/directive`, then `directive install --repair`.
|
|
62
64
|
|
|
63
65
|
**Upgrade version or migrate data:** `directive upgrade` (alias: `update`).
|
package/docs/scope-provenance.md
CHANGED
|
@@ -45,10 +45,24 @@ Allowance = clamp(count of **concrete** production files on the merge-base `file
|
|
|
45
45
|
When the bound story is in the change set:
|
|
46
46
|
|
|
47
47
|
1. A human-stamped merge-base mint for the **continuity-resolved** story is the allowlist when present (planId match ignores stale `xbriefRelPath`; no-`plan.id` is path-first + basename-keyed mint with matching `xbriefRelPath`).
|
|
48
|
-
2. Otherwise membership evaluates against the merge-base brief's **concrete** `file_scope` as agent precommitment. Head brief cannot widen.
|
|
48
|
+
2. Otherwise membership evaluates against the merge-base brief's **concrete** `file_scope` as agent precommitment. Head brief cannot widen, except under a qualifying forge approval (#6203, below).
|
|
49
49
|
3. Free paths: bound brief, verified peer briefs, `CHANGELOG.md`, and continuity lifecycle paths of the bound identity. An xBRIEF-only change set may omit a mint.
|
|
50
50
|
4. Empty / invalid / unreadable mint records fail closed (they are not absence).
|
|
51
51
|
|
|
52
|
+
#### Forge-approved head brief (#6203)
|
|
53
|
+
|
|
54
|
+
A PR can carry a new or widened brief plus its code when a human approves it on GitHub. With PR context (`--pr N` / `--repo OWNER/NAME`, or `GITHUB_PR_NUMBER` / `GITHUB_REPOSITORY` in CI), a failing membership check under precommitment or missing authority consults the forge. When a qualifying approval exists, membership evaluates against the **head** brief's concrete `file_scope` instead of the merge-base brief.
|
|
55
|
+
|
|
56
|
+
- **Mint wins.** A present mint keeps the mint path. The forge is never consulted.
|
|
57
|
+
- **Auth-hash v1** (`sha256:` over canonical JSON; strings byte-exact, no Unicode normalization) covers `plan.id`, the xbrief path, the concrete `file_scope`, and the acceptanceCriteria array: `plan.acceptance`, `plan.narratives.AcceptanceCriteria`, and each item's `narrative.Acceptance`, in document order. `productPullRequest` and other delivery metadata stay out, so a PPR stamp keeps the approval. Any change to the covered fields drops it.
|
|
58
|
+
- **Qualifying approval:** the reviewer's latest deciding review (APPROVED / CHANGES_REQUESTED / DISMISSED) is APPROVED; the reviewer is a `User` (not Bot or App), not the PR author, and has `write`, `maintain` or `admin` on the repo; the repo is the checkout's `origin` (a `--repo` value may only confirm it, so a fork's approval never counts) and is the PR's base repo; the evaluated HEAD is the PR head (or GitHub's merge commit whose second parent is the PR head); and the brief at the reviewed commit hashes the same as the head brief.
|
|
59
|
+
- **Fallback:** missing PR context, no qualifying approval, a hash mismatch, or an unavailable forge keeps today's Path B or mint finding unchanged. The result message adds one `forge approval (#6203)` line with the reason.
|
|
60
|
+
- **Membership only.** The production fence still reads the merge-base brief. A widened brief can pass membership and still fail `production-scope-over-budget`. A brand-new brief has no production fence (as today).
|
|
61
|
+
- **Re-run after approval.** Approval does not re-trigger CI. Re-run the merge gate (or push) after the approval lands.
|
|
62
|
+
- **Residual (#6207):** an agent that holds a second human forge token can satisfy every predicate above. The hash binds the approved bytes; it does not prove independent human intent and does not close that confused-deputy risk. That posture stays open on #6207.
|
|
63
|
+
|
|
64
|
+
Code: `packages/core/src/scope-provenance/forge-approval.ts`.
|
|
65
|
+
|
|
52
66
|
#### Path A planning split (#5551)
|
|
53
67
|
|
|
54
68
|
When an active implementation brief is **outside** the change set, membership is a no-op for that identity (Path A). Supported planning publication is two change sets, not one:
|
package/package.json
CHANGED