devflow-kit 2.3.0 → 2.5.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/CHANGELOG.md +177 -0
- package/README.md +86 -18
- package/dist/agents/git.md +824 -0
- package/dist/cli/commands/agents.js +6 -1
- package/dist/cli/commands/attribution-prompts.js +144 -0
- package/dist/cli/commands/compliance-prompts.js +7 -11
- package/dist/cli/commands/compliance.js +23 -1
- package/dist/cli/commands/init-seed.js +69 -30
- package/dist/cli/commands/init.js +551 -77
- package/dist/cli/commands/install-report.js +205 -0
- package/dist/cli/commands/knowledge/index.js +2 -2
- package/dist/cli/commands/knowledge/toggle.js +27 -37
- package/dist/cli/commands/learning.js +37 -30
- package/dist/cli/commands/memory.js +79 -69
- package/dist/cli/commands/prompt-io.js +38 -0
- package/dist/cli/commands/proxy.js +91 -31
- package/dist/cli/commands/security.js +76 -16
- package/dist/cli/commands/skills.js +53 -7
- package/dist/cli/commands/tracker-prompts.js +145 -0
- package/dist/cli/commands/tracker.js +405 -0
- package/dist/cli/commands/uninstall.js +233 -77
- package/dist/cli.js +2 -0
- package/dist/commands/bug-analysis.md +22 -4
- package/dist/commands/code-review.md +44 -15
- package/dist/commands/debug.md +20 -6
- package/dist/commands/dynamic-build.md +289 -67
- package/dist/commands/dynamic-plan.md +60 -21
- package/dist/commands/dynamic-profile.md +1 -1
- package/dist/commands/dynamic-tickets.md +58 -8
- package/dist/commands/explore.md +2 -2
- package/dist/commands/implement.md +241 -53
- package/dist/commands/plan.md +88 -17
- package/dist/commands/release.md +64 -17
- package/dist/commands/resolve.md +138 -58
- package/dist/commands/self-review.md +2 -2
- package/dist/core/agent-models.js +55 -12
- package/dist/core/assets.js +58 -2
- package/dist/core/evidence-policy.js +147 -0
- package/dist/core/feature-config.js +130 -64
- package/dist/core/feature-switch.js +112 -0
- package/dist/core/flags.js +149 -8
- package/dist/core/manifest.js +33 -7
- package/dist/core/mds-variants.js +861 -0
- package/dist/core/model-discovery.js +12 -1
- package/dist/core/plugins.js +357 -9
- package/dist/core/project-paths.js +1 -1
- package/dist/core/proxy-log.js +8 -4
- package/dist/core/proxy-state.js +100 -43
- package/dist/core/reference-sweep.js +136 -0
- package/dist/core/tracker.js +407 -0
- package/dist/skills/git/references/decision-markers.md +19 -0
- package/dist/skills/git/references/learn-conventions.md +56 -0
- package/dist/skills/git/references/pr/check-ci-status.md +14 -0
- package/dist/skills/git/references/pr/check-merge-readiness.md +28 -0
- package/dist/skills/git/references/pr/ensure-pr-ready.md +24 -0
- package/dist/skills/git/references/pr/fetch-review-threads.md +22 -0
- package/dist/skills/git/references/pr/post-resolution-summary.md +40 -0
- package/dist/skills/git/references/pr/post-review-summary.md +42 -0
- package/dist/skills/git/references/pr/resolve-review-threads.md +35 -0
- package/dist/skills/git/references/pr/update-pr-evidence.md +14 -0
- package/dist/skills/git/references/pr/validate-branch.md +18 -0
- package/dist/skills/git/references/publication-gate.md +13 -0
- package/dist/skills/git/references/tracker/_mcp.md +153 -0
- package/dist/skills/git/references/tracker/github/associate-release.md +18 -0
- package/dist/skills/git/references/tracker/github/backlink-shipped-issues.md +40 -0
- package/dist/skills/git/references/tracker/github/create-release.md +11 -0
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +16 -0
- package/dist/skills/git/references/tracker/github/ensure-traceable-issue.md +69 -0
- package/dist/skills/git/references/tracker/github/fetch-issue.md +32 -0
- package/dist/skills/git/references/tracker/github/fetch-issues-batch.md +17 -0
- package/dist/skills/git/references/tracker/github/gather-release-evidence.md +19 -0
- package/dist/skills/git/references/tracker/github/manage-debt.md +101 -0
- package/dist/skills/git/references/tracker/github/post-wave-report.md +28 -0
- package/dist/skills/git/references/tracker/github/setup-task.md +26 -0
- package/dist/skills/git/references/tracker/jira/associate-release.md +18 -0
- package/dist/skills/git/references/tracker/jira/backlink-shipped-issues.md +49 -0
- package/dist/skills/git/references/tracker/jira/create-release.md +17 -0
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +22 -0
- package/dist/skills/git/references/tracker/jira/ensure-traceable-issue.md +53 -0
- package/dist/skills/git/references/tracker/jira/fetch-issue.md +14 -0
- package/dist/skills/git/references/tracker/jira/fetch-issues-batch.md +15 -0
- package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +18 -0
- package/dist/skills/git/references/tracker/jira/manage-debt.md +37 -0
- package/dist/skills/git/references/tracker/jira/post-wave-report.md +33 -0
- package/dist/skills/git/references/tracker/jira/setup-task.md +31 -0
- package/dist/skills/git/references/tracker/linear/associate-release.md +18 -0
- package/dist/skills/git/references/tracker/linear/backlink-shipped-issues.md +53 -0
- package/dist/skills/git/references/tracker/linear/create-release.md +17 -0
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +22 -0
- package/dist/skills/git/references/tracker/linear/ensure-traceable-issue.md +53 -0
- package/dist/skills/git/references/tracker/linear/fetch-issue.md +14 -0
- package/dist/skills/git/references/tracker/linear/fetch-issues-batch.md +15 -0
- package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +18 -0
- package/dist/skills/git/references/tracker/linear/manage-debt.md +37 -0
- package/dist/skills/git/references/tracker/linear/post-wave-report.md +33 -0
- package/dist/skills/git/references/tracker/linear/setup-task.md +32 -0
- package/dist/skills/git/references/trust-rule.md +7 -0
- package/dist/targets/claude-code/installer.js +1213 -31
- package/dist/targets/claude-code/legacy.js +5 -0
- package/dist/targets/claude-code/post-install.js +303 -104
- package/dist/targets/claude-code/tracker-install.js +161 -0
- package/package.json +4 -3
- package/src/assets/agents/code.md +42 -4
- package/src/assets/agents/design.md +1 -1
- package/src/assets/agents/git.mds +827 -0
- package/src/assets/agents/knowledge.md +1 -1
- package/src/assets/agents/learning.md +11 -0
- package/src/assets/agents/synthesize.md +1 -1
- package/src/assets/agents/test.md +16 -5
- package/src/assets/agents/tracker.md +467 -0
- package/src/assets/agents/validate.md +7 -5
- package/src/assets/commands/_partials/_engine.mds +11 -9
- package/src/assets/commands/_partials/_evidence_policy.mds +30 -0
- package/src/assets/commands/_partials/_knowledge.mds +2 -2
- package/src/assets/commands/_partials/_plan_contract.mds +22 -7
- package/src/assets/commands/_partials/_preamble.mds +1 -1
- package/src/assets/commands/_partials/_publication.mds +3 -1
- package/src/assets/commands/_partials/_ticket_template.mds +3 -2
- package/src/assets/commands/_partials/_tracker.mds +18 -0
- package/src/assets/commands/_partials/_wave.mds +16 -10
- package/src/assets/commands/bug-analysis.mds +15 -5
- package/src/assets/commands/code-review.mds +34 -14
- package/src/assets/commands/debug.mds +11 -4
- package/src/assets/commands/dynamic-build.mds +227 -41
- package/src/assets/commands/dynamic-plan.mds +35 -13
- package/src/assets/commands/dynamic-tickets.mds +47 -5
- package/src/assets/commands/implement.mds +206 -52
- package/src/assets/commands/plan.mds +70 -17
- package/src/assets/commands/release.md +64 -17
- package/src/assets/commands/resolve.mds +126 -56
- package/src/assets/mds/git/_pr.mds +331 -0
- package/src/assets/mds/git/_references.mds +135 -0
- package/src/assets/mds/tracker/_common.mds +156 -0
- package/src/assets/mds/tracker/_github.mds +472 -0
- package/src/assets/mds/tracker/_jira.mds +407 -0
- package/src/assets/mds/tracker/_linear.mds +449 -0
- package/src/assets/mds/tracker/_mcp.mds +299 -0
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +5 -8
- package/src/assets/scripts/hooks/background-memory-update +14 -9
- package/src/assets/scripts/hooks/capture-prompt +6 -2
- package/src/assets/scripts/hooks/capture-question +6 -2
- package/src/assets/scripts/hooks/capture-turn +6 -2
- package/src/assets/scripts/hooks/ensure-devflow-init +1 -1
- package/src/assets/scripts/hooks/ensure-proxy +64 -5
- package/src/assets/scripts/hooks/ensure-root-gitignore +161 -60
- package/src/assets/scripts/hooks/hook-log-init +3 -1
- package/src/assets/scripts/hooks/json-helper.cjs +223 -5
- package/src/assets/scripts/hooks/lib/project-paths.cjs +1 -1
- package/src/assets/scripts/hooks/memory-worker +15 -8
- package/src/assets/scripts/hooks/pre-compact-memory +12 -8
- package/src/assets/scripts/hooks/preamble +1 -4
- package/src/assets/scripts/hooks/queue-append +68 -24
- package/src/assets/scripts/hooks/session-start-context +355 -8
- package/src/assets/scripts/hooks/session-start-memory +12 -8
- package/src/assets/scripts/pr-evidence.cjs +1961 -0
- package/src/assets/scripts/redact-secrets.cjs +490 -62
- package/src/assets/scripts/release-trace.cjs +1143 -0
- package/src/assets/scripts/resolve-evidence-policy.cjs +1065 -0
- package/src/assets/scripts/verify-evidence.cjs +1822 -0
- package/src/assets/skills/compliance/SKILL.md +2 -0
- package/src/assets/skills/docs-framework/SKILL.md +5 -3
- package/src/assets/skills/git/SKILL.md +8 -82
- package/src/assets/skills/git/references/github-api.md +179 -141
- package/src/assets/skills/git/references/patterns.md +11 -12
- package/src/assets/skills/review-methodology/SKILL.md +1 -1
- package/src/assets/skills/review-methodology/references/patterns.md +6 -61
- package/src/assets/skills/review-methodology/references/violations.md +14 -22
- package/src/targets/claude-code/templates/settings.json +0 -4
- package/src/assets/agents/git.md +0 -938
package/dist/core/flags.js
CHANGED
|
@@ -11,6 +11,17 @@
|
|
|
11
11
|
* point every caller reaches). applyFlags(settingsJson, FlagsRecord) is the
|
|
12
12
|
* sole API; init.ts works directly with FlagsRecord (no legacy string[] bridge).
|
|
13
13
|
*/
|
|
14
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
15
|
+
/**
|
|
16
|
+
* Type predicate that narrows `ClaudeCodeFlag` to `EnvBooleanFlagDef`.
|
|
17
|
+
* Narrows `f.onPayload` to `string` so callers writing to the env string-map
|
|
18
|
+
* avoid an `unknown` hop without an unsafe cast.
|
|
19
|
+
*
|
|
20
|
+
* Use in `buildPayload` and `applyFlags` only where the string type is load-bearing.
|
|
21
|
+
*/
|
|
22
|
+
export function isEnvBooleanFlag(f) {
|
|
23
|
+
return f.kind === 'boolean' && f.target.type === 'env';
|
|
24
|
+
}
|
|
14
25
|
// ─── Registry ─────────────────────────────────────────────────────────────────
|
|
15
26
|
// Phase 0 probe findings: see docs/reference/claude-code-flags-probe.md
|
|
16
27
|
/**
|
|
@@ -110,8 +121,8 @@ export const FLAG_REGISTRY = [
|
|
|
110
121
|
kind: 'boolean',
|
|
111
122
|
target: { type: 'setting', key: 'disableBundledSkills' },
|
|
112
123
|
onPayload: true,
|
|
113
|
-
recommended:
|
|
114
|
-
defaultValue:
|
|
124
|
+
recommended: false,
|
|
125
|
+
defaultValue: false,
|
|
115
126
|
},
|
|
116
127
|
{
|
|
117
128
|
id: 'pin-sonnet-4-6',
|
|
@@ -122,8 +133,8 @@ export const FLAG_REGISTRY = [
|
|
|
122
133
|
kind: 'boolean',
|
|
123
134
|
target: { type: 'env', key: 'ANTHROPIC_DEFAULT_SONNET_MODEL' },
|
|
124
135
|
onPayload: 'claude-sonnet-4-6',
|
|
125
|
-
recommended:
|
|
126
|
-
defaultValue:
|
|
136
|
+
recommended: false,
|
|
137
|
+
defaultValue: false,
|
|
127
138
|
},
|
|
128
139
|
{
|
|
129
140
|
// Devflow fan-outs routinely exceed the upstream default of 20.
|
|
@@ -309,6 +320,24 @@ export const FLAG_REGISTRY = [
|
|
|
309
320
|
recommended: false,
|
|
310
321
|
defaultValue: false,
|
|
311
322
|
},
|
|
323
|
+
{
|
|
324
|
+
// D27: gates Claude Code's AI-attribution injection into git commits and PRs.
|
|
325
|
+
// When true, writes {"commit":"","pr":""} to settings.json which suppresses attribution.
|
|
326
|
+
// When false/null (neutral), removes the key ONLY when the current value is the exact
|
|
327
|
+
// devflow-managed shape (settingDeleteGuard). A custom attribution value (e.g. an org
|
|
328
|
+
// name) is never deleted — shape guard prevents erasure (D-ATTR-GUARD).
|
|
329
|
+
id: 'suppress-attribution',
|
|
330
|
+
label: 'Suppress AI attribution',
|
|
331
|
+
description: 'Remove AI-attribution labels from git commits and pull requests',
|
|
332
|
+
hint: 'Suppresses Claude attribution labels in git commits and pull requests',
|
|
333
|
+
blurb: 'hide AI attribution labels',
|
|
334
|
+
kind: 'boolean',
|
|
335
|
+
target: { type: 'setting', key: 'attribution' },
|
|
336
|
+
onPayload: { commit: '', pr: '' },
|
|
337
|
+
settingDeleteGuard: { commit: '', pr: '' },
|
|
338
|
+
recommended: false,
|
|
339
|
+
defaultValue: false,
|
|
340
|
+
},
|
|
312
341
|
// ── Valued flags (number/enum/string) ────────────────────────────────────
|
|
313
342
|
{
|
|
314
343
|
// Domain: unset by default; set only when users want a non-default spawn depth.
|
|
@@ -837,6 +866,78 @@ export function migrateLegacyFlagsToRecord(enabledIds, knownIds, legacyViewMode)
|
|
|
837
866
|
return result;
|
|
838
867
|
}
|
|
839
868
|
// ─── Apply / Strip ────────────────────────────────────────────────────────────
|
|
869
|
+
/**
|
|
870
|
+
* Returns true when `value` equals the managed shape declared by `flag.settingDeleteGuard`.
|
|
871
|
+
* Returns false when: the flag has no guard, is not a setting-target boolean, or the value
|
|
872
|
+
* does not deep-equal the guard.
|
|
873
|
+
*
|
|
874
|
+
* Single equality oracle for managed-shape comparisons — used by `canDeleteSettingKey`,
|
|
875
|
+
* `settingHoldsManagedShape`, and the Step 2b adoption fold in `convergeFlagsIntoSettings`.
|
|
876
|
+
* All three delegate here so there is exactly one `isDeepStrictEqual` call for guard matching.
|
|
877
|
+
*/
|
|
878
|
+
export function settingValueHoldsManagedShape(flag, value) {
|
|
879
|
+
if (flag.kind !== 'boolean' || flag.target.type !== 'setting')
|
|
880
|
+
return false;
|
|
881
|
+
const guard = flag.settingDeleteGuard;
|
|
882
|
+
if (guard === undefined)
|
|
883
|
+
return false;
|
|
884
|
+
return isDeepStrictEqual(value, guard);
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* D-ATTR-GUARD single-source predicate (consistency-01 / ADR-024).
|
|
888
|
+
*
|
|
889
|
+
* Returns true when the settings.json string contains a value at the flag's
|
|
890
|
+
* target key that equals the flag's managed shape (`settingDeleteGuard`).
|
|
891
|
+
*
|
|
892
|
+
* This is the ONE place that decides "on-disk value equals the managed shape".
|
|
893
|
+
* All consumers — `resolveExistingAttributionSuppression` (seeding priority),
|
|
894
|
+
* and the guarded-boolean adoption fold in `convergeFlagsIntoSettings` — delegate
|
|
895
|
+
* here rather than hand-rolling their own comparison.
|
|
896
|
+
*
|
|
897
|
+
* Returns false when:
|
|
898
|
+
* - settingsJson is malformed
|
|
899
|
+
* - root is not a plain object
|
|
900
|
+
* - flagId is not in the registry
|
|
901
|
+
* - the flag has no settingDeleteGuard
|
|
902
|
+
* - the flag is not a setting-target boolean
|
|
903
|
+
* - the on-disk value does not deep-equal the guard
|
|
904
|
+
*/
|
|
905
|
+
export function settingHoldsManagedShape(settingsJson, flagId) {
|
|
906
|
+
try {
|
|
907
|
+
const parsed = JSON.parse(settingsJson);
|
|
908
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
909
|
+
return false;
|
|
910
|
+
const flag = FLAG_REGISTRY_MAP.get(flagId);
|
|
911
|
+
if (!flag)
|
|
912
|
+
return false;
|
|
913
|
+
const value = parsed[flag.target.key];
|
|
914
|
+
return settingValueHoldsManagedShape(flag, value);
|
|
915
|
+
}
|
|
916
|
+
catch {
|
|
917
|
+
return false;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* D-ATTR-GUARD: returns true when it is safe to delete a flag's target key from
|
|
922
|
+
* settings. For flags with a settingDeleteGuard the key is only deleted when the
|
|
923
|
+
* current on-disk value deep-equals the managed shape, preventing erasure of
|
|
924
|
+
* user-customized values (e.g. an organisation attribution block) on flag
|
|
925
|
+
* disable or uninstall. Flags without a guard are always safe to delete.
|
|
926
|
+
*
|
|
927
|
+
* Delegates to `settingValueHoldsManagedShape` — the single equality oracle for
|
|
928
|
+
* managed-shape comparisons — so there is exactly one `isDeepStrictEqual` call
|
|
929
|
+
* across the entire apply/strip pipeline (ADR-024 mechanism 3).
|
|
930
|
+
*
|
|
931
|
+
* Single-source invariant: both the disable path (applyFlags neutral branch) and
|
|
932
|
+
* the uninstall path (stripFlags) collapse to this predicate.
|
|
933
|
+
*/
|
|
934
|
+
function canDeleteSettingKey(flag, settings) {
|
|
935
|
+
if (flag.kind !== 'boolean')
|
|
936
|
+
return true;
|
|
937
|
+
if (flag.settingDeleteGuard === undefined)
|
|
938
|
+
return true;
|
|
939
|
+
return settingValueHoldsManagedShape(flag, settings[flag.target.key]);
|
|
940
|
+
}
|
|
840
941
|
/**
|
|
841
942
|
* Return `v` as a `Record<string, unknown>` only when it is a plain object.
|
|
842
943
|
* Returns undefined for arrays, null, or non-objects.
|
|
@@ -853,8 +954,17 @@ function asPlainObject(v) {
|
|
|
853
954
|
/** Compute the value to write to settings.json for an active flag. */
|
|
854
955
|
function buildPayload(flag, value) {
|
|
855
956
|
switch (flag.kind) {
|
|
856
|
-
case 'boolean':
|
|
857
|
-
return
|
|
957
|
+
case 'boolean': {
|
|
958
|
+
// D-PAYLOAD-CLONE: return a structural clone for object payloads so the
|
|
959
|
+
// FLAG_REGISTRY entry is never aliased into the caller's settings tree.
|
|
960
|
+
// Primitives (string, boolean) are values and require no clone.
|
|
961
|
+
// isEnvBooleanFlag narrows onPayload to string for env flags — no clone needed,
|
|
962
|
+
// and the string type avoids an `unknown` hop into the env string-map.
|
|
963
|
+
if (isEnvBooleanFlag(flag))
|
|
964
|
+
return flag.onPayload; // string — no clone needed
|
|
965
|
+
const p = flag.onPayload;
|
|
966
|
+
return typeof p === 'object' && p !== null ? structuredClone(p) : p;
|
|
967
|
+
}
|
|
858
968
|
case 'enum':
|
|
859
969
|
return value;
|
|
860
970
|
case 'number':
|
|
@@ -904,7 +1014,8 @@ export function applyFlags(settingsJson, flags) {
|
|
|
904
1014
|
delete env[flag.target.key];
|
|
905
1015
|
}
|
|
906
1016
|
else {
|
|
907
|
-
|
|
1017
|
+
if (canDeleteSettingKey(flag, settings))
|
|
1018
|
+
delete settings[flag.target.key];
|
|
908
1019
|
}
|
|
909
1020
|
}
|
|
910
1021
|
else {
|
|
@@ -949,7 +1060,8 @@ export function stripFlags(settingsJson) {
|
|
|
949
1060
|
delete env[flag.target.key];
|
|
950
1061
|
}
|
|
951
1062
|
else {
|
|
952
|
-
|
|
1063
|
+
if (canDeleteSettingKey(flag, settings))
|
|
1064
|
+
delete settings[flag.target.key];
|
|
953
1065
|
}
|
|
954
1066
|
}
|
|
955
1067
|
if (env && Object.keys(env).length === 0) {
|
|
@@ -1116,6 +1228,35 @@ export function convergeFlagsIntoSettings(settingsJson, record, opts) {
|
|
|
1116
1228
|
folded[flag.id] = coerced;
|
|
1117
1229
|
}
|
|
1118
1230
|
}
|
|
1231
|
+
// ── Step 2b: adopt guarded boolean settings before the strip ─────────────
|
|
1232
|
+
// D-ATTR-ADOPT (PF-050 / ADR-024): a delete guard is evidence about the VALUE,
|
|
1233
|
+
// never about the record — a pre-existing on-disk key whose value matches the
|
|
1234
|
+
// managed shape means devflow wrote it, so adopt it into the record now, before
|
|
1235
|
+
// stripFlags can unconditionally remove it on the next line.
|
|
1236
|
+
//
|
|
1237
|
+
// Mirror of the view-mode fold (Step 1): adopt only when:
|
|
1238
|
+
// (a) the flag has a settingDeleteGuard (guarded boolean only)
|
|
1239
|
+
// (b) the record does NOT already claim the key — a claimed false or null still
|
|
1240
|
+
// deletes the managed block (the user intentionally disabled it)
|
|
1241
|
+
// (c) the pre-strip on-disk value holds the managed shape
|
|
1242
|
+
//
|
|
1243
|
+
// This fold runs before stripFlags so applyFlags rewrites the block from the
|
|
1244
|
+
// now-claimed record and the block survives on BOTH the init and flags paths.
|
|
1245
|
+
for (const flag of FLAG_REGISTRY) {
|
|
1246
|
+
if (flag.kind !== 'boolean')
|
|
1247
|
+
continue;
|
|
1248
|
+
if (flag.target.type !== 'setting')
|
|
1249
|
+
continue;
|
|
1250
|
+
// Skip when the record already claims this flag (claimed false/null deletes the block)
|
|
1251
|
+
const claimed = claimedIn !== null &&
|
|
1252
|
+
Object.prototype.hasOwnProperty.call(claimedIn, flag.id);
|
|
1253
|
+
if (claimed)
|
|
1254
|
+
continue;
|
|
1255
|
+
// Adopt only when the on-disk value matches the managed shape exactly
|
|
1256
|
+
if (settingValueHoldsManagedShape(flag, parsed[flag.target.key])) {
|
|
1257
|
+
folded[flag.id] = true;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1119
1260
|
// ── Step 3: strip all managed keys, then apply the folded record ──────────
|
|
1120
1261
|
const stripped = stripFlags(settingsJson);
|
|
1121
1262
|
const settings = applyFlags(stripped, folded);
|
package/dist/core/manifest.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as path from 'path';
|
|
|
3
3
|
import { LEGACY_PLUGIN_NAMES, DELETED_PLUGIN_NAMES } from './plugins.js';
|
|
4
4
|
import { VIEW_MODES, migrateLegacyFlagsToRecord, sanitizeFlagsRecord, } from './flags.js';
|
|
5
5
|
import { normalizeComplianceFeature } from './compliance.js';
|
|
6
|
+
import { normalizeTrackerFeature } from './tracker.js';
|
|
6
7
|
import { writeFileAtomicExclusive } from './fs-atomic.js';
|
|
7
8
|
/**
|
|
8
9
|
* Parse features.flags across the three on-disk shapes; reports whether a heal is owed.
|
|
@@ -54,13 +55,14 @@ function parseManifestFlags(features, knownFlags) {
|
|
|
54
55
|
* Read and parse the manifest file. Returns null if missing or corrupt.
|
|
55
56
|
*
|
|
56
57
|
* Self-heals the following on-disk inconsistencies (applies ADR-014):
|
|
57
|
-
* - features.kb → features.knowledge rename
|
|
58
|
-
* - features.decisions → features.learning rename
|
|
58
|
+
* - features.kb → features.knowledge rename (both absent → true, D-FEATURES-ABSENT-ON)
|
|
59
|
+
* - features.decisions → features.learning rename (both absent → true, D-FEATURES-ABSENT-ON)
|
|
59
60
|
* - features.flags as string[] → FlagsRecord (via migrateLegacyFlagsToRecord)
|
|
60
61
|
* - features.viewMode folded into flags['view-mode'] and stripped from result
|
|
61
62
|
* - features.knownFlags stripped from result (folded into FlagsRecord key-presence)
|
|
62
63
|
* - features.proxy absent → false
|
|
63
64
|
* - features.compliance absent/malformed → {enabled:false, frameworks:[]}
|
|
65
|
+
* - features.tracker absent/malformed → {provider:'github'}
|
|
64
66
|
*
|
|
65
67
|
* D39: heal-write failure returns the migrated in-memory manifest (not null).
|
|
66
68
|
* The on-disk format remains unhealed; next read triggers another attempt.
|
|
@@ -82,15 +84,33 @@ export async function readManifest(devflowDir) {
|
|
|
82
84
|
typeof data.updatedAt !== 'string') {
|
|
83
85
|
return null;
|
|
84
86
|
}
|
|
85
|
-
//
|
|
87
|
+
// D-FEATURES-ABSENT-ON (a sub-decision of D-FEATURES-MACHINE-WIDE,
|
|
88
|
+
// src/core/feature-switch.ts): knowledge and learning are machine-wide
|
|
89
|
+
// switches, and every runtime gate — queue_read_gates in the hooks,
|
|
90
|
+
// isMachineFeatureOn in the CLI, the knowledge write-back prose gate — reads
|
|
91
|
+
// an ABSENT key as ON. This reader must agree: init seeds from it and every
|
|
92
|
+
// syncManifestFeature toggle writes its result back, so a `false` default
|
|
93
|
+
// here would persist "off" for a feature the runtime is running — a plain
|
|
94
|
+
// re-init or `devflow hud --enable` on an older manifest silently switching
|
|
95
|
+
// it off machine-wide. Explicit booleans (and their legacy names) are kept
|
|
96
|
+
// as they are. memory needs no default: it is in the hard-null set above,
|
|
97
|
+
// so a manifest without it reads as no prior install and init seeds
|
|
98
|
+
// FEATURE_DEFAULTS.memory (true).
|
|
99
|
+
//
|
|
100
|
+
// Self-heal: rename features.kb → features.knowledge on disk.
|
|
101
|
+
// Coalesce: features.knowledge wins; fall back to features.kb; default ON.
|
|
102
|
+
// D-KNOWLEDGE-LEGACY-KB: isMachineFeatureOn applies this exact precedence,
|
|
103
|
+
// so the legacy key is honoured before the heal lands.
|
|
86
104
|
const knowledge = typeof features.knowledge === 'boolean' ? features.knowledge
|
|
87
105
|
: typeof features.kb === 'boolean' ? features.kb
|
|
88
|
-
:
|
|
89
|
-
// Self-heal: rename features.decisions → features.learning on disk
|
|
90
|
-
// Coalesce: features.learning wins; fall back to features.decisions; default
|
|
106
|
+
: true;
|
|
107
|
+
// Self-heal: rename features.decisions → features.learning on disk (ADR-011).
|
|
108
|
+
// Coalesce: features.learning wins; fall back to features.decisions; default ON.
|
|
109
|
+
// D-LEARNING-LEGACY-DECISIONS: isMachineFeatureOn and queue_read_gates apply
|
|
110
|
+
// this exact precedence, so the legacy key is honoured before the heal lands.
|
|
91
111
|
const learning = typeof features.learning === 'boolean' ? features.learning
|
|
92
112
|
: typeof features.decisions === 'boolean' ? features.decisions
|
|
93
|
-
:
|
|
113
|
+
: true;
|
|
94
114
|
// Self-heal: non-string-array or absent knownFlags/knownPlugins → undefined (never partial/garbage)
|
|
95
115
|
const asStringArray = (val) => Array.isArray(val) && val.every(e => typeof e === 'string')
|
|
96
116
|
? val
|
|
@@ -138,6 +158,12 @@ export async function readManifest(devflowDir) {
|
|
|
138
158
|
proxy: typeof features.proxy === 'boolean' ? features.proxy : false,
|
|
139
159
|
// Self-heal: absent/malformed compliance → {enabled:false, frameworks:[]}
|
|
140
160
|
compliance: normalizeComplianceFeature(features.compliance),
|
|
161
|
+
// Self-heal: absent/malformed/unknown tracker → {provider:'github'} (AC-3.21).
|
|
162
|
+
// Deliberately NOT in the hard-null set above — see the field's doc comment.
|
|
163
|
+
// Healing here is silent and emits no DEGRADED: that is the correct
|
|
164
|
+
// ADR-014 behaviour, and a different condition from a per-repo config
|
|
165
|
+
// value outside the domain (which does emit `unknown tracker provider`).
|
|
166
|
+
tracker: normalizeTrackerFeature(features.tracker),
|
|
141
167
|
},
|
|
142
168
|
installedAt: data.installedAt,
|
|
143
169
|
updatedAt: data.updatedAt,
|