@codedrifters/configulator 0.0.301 → 0.0.302
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/lib/index.d.mts +256 -1
- package/lib/index.d.ts +257 -2
- package/lib/index.js +563 -10
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +554 -10
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
package/lib/index.mjs
CHANGED
|
@@ -2898,6 +2898,457 @@ function renderStubIndexConventionRuleContent() {
|
|
|
2898
2898
|
].join("\n");
|
|
2899
2899
|
}
|
|
2900
2900
|
|
|
2901
|
+
// src/agent/bundles/temporal-framing.ts
|
|
2902
|
+
var DEFAULT_TEMPORAL_FRAMING_ENABLED = true;
|
|
2903
|
+
var DEFAULT_TEMPORAL_FRAMING_PATHS = [
|
|
2904
|
+
"docs/src/content/docs/profiles/**/*.md",
|
|
2905
|
+
"docs/src/content/docs/industry-research/**/*.md",
|
|
2906
|
+
"docs/src/content/docs/software-research/**/*.md",
|
|
2907
|
+
"docs/src/content/docs/regulatory-research/**/*.md",
|
|
2908
|
+
"docs/src/content/docs/standards-research/**/*.md",
|
|
2909
|
+
"docs/src/content/docs/customer-research/**/*.md"
|
|
2910
|
+
];
|
|
2911
|
+
var TEMPORAL_FRAMING_CATEGORY_VALUES = [
|
|
2912
|
+
"ownership",
|
|
2913
|
+
"company-leadership",
|
|
2914
|
+
"regulatory-status",
|
|
2915
|
+
"litigation",
|
|
2916
|
+
"dated-metrics"
|
|
2917
|
+
];
|
|
2918
|
+
var DEFAULT_TEMPORAL_FRAMING_CADENCES = {
|
|
2919
|
+
ownership: 180,
|
|
2920
|
+
"company-leadership": 90,
|
|
2921
|
+
"regulatory-status": 30,
|
|
2922
|
+
litigation: 30,
|
|
2923
|
+
"dated-metrics": 180
|
|
2924
|
+
};
|
|
2925
|
+
var DEFAULT_TEMPORAL_FRAMING_EMIT_CHECKER = false;
|
|
2926
|
+
function resolveTemporalFraming(config) {
|
|
2927
|
+
const paths = config?.paths ?? DEFAULT_TEMPORAL_FRAMING_PATHS;
|
|
2928
|
+
assertValidPaths(paths);
|
|
2929
|
+
const cadences = resolveCadences(config?.cadences);
|
|
2930
|
+
return {
|
|
2931
|
+
enabled: config?.enabled ?? DEFAULT_TEMPORAL_FRAMING_ENABLED,
|
|
2932
|
+
paths,
|
|
2933
|
+
cadences,
|
|
2934
|
+
emitChecker: config?.emitChecker ?? DEFAULT_TEMPORAL_FRAMING_EMIT_CHECKER
|
|
2935
|
+
};
|
|
2936
|
+
}
|
|
2937
|
+
function validateTemporalFramingConfig(config) {
|
|
2938
|
+
return resolveTemporalFraming(config);
|
|
2939
|
+
}
|
|
2940
|
+
function renderTemporalFramingRuleContent(tf) {
|
|
2941
|
+
if (!tf.enabled) {
|
|
2942
|
+
return [
|
|
2943
|
+
"# Temporal Framing Convention",
|
|
2944
|
+
"",
|
|
2945
|
+
"**Temporal framing is not enforced in this project.** Agents are",
|
|
2946
|
+
"not required to add `as of [date]` qualifiers to time-sensitive",
|
|
2947
|
+
"claims, and refresh passes do not use a mechanical grep to find",
|
|
2948
|
+
"stale framing. Staleness is caught by review and downstream user",
|
|
2949
|
+
"reports alone.",
|
|
2950
|
+
"",
|
|
2951
|
+
"Enable the convention via",
|
|
2952
|
+
"`AgentConfigOptions.temporalFraming.enabled = true`."
|
|
2953
|
+
].join("\n");
|
|
2954
|
+
}
|
|
2955
|
+
const lines = [
|
|
2956
|
+
"# Temporal Framing Convention",
|
|
2957
|
+
"",
|
|
2958
|
+
"Every factual claim that can change over time MUST carry an inline",
|
|
2959
|
+
"`as of [YYYY-MM-DD]` or `as of [Month YYYY]` qualifier on the",
|
|
2960
|
+
"**first** occurrence of the claim in the document. Once qualified",
|
|
2961
|
+
"once, subsequent references to the same fact in the same document",
|
|
2962
|
+
"do not need to repeat the qualifier \u2014 the first qualifier governs",
|
|
2963
|
+
"the document's snapshot date for that fact.",
|
|
2964
|
+
"",
|
|
2965
|
+
"The qualifier is mechanical, not editorial. Refresh agents grep",
|
|
2966
|
+
"for `as of ` to find the time-bounded claims; without the",
|
|
2967
|
+
"qualifier, a refresh pass has no cheap way to tell stale framing",
|
|
2968
|
+
"from stable facts.",
|
|
2969
|
+
"",
|
|
2970
|
+
"## What counts as time-sensitive",
|
|
2971
|
+
"",
|
|
2972
|
+
"A claim is time-sensitive (and must carry the qualifier) when it",
|
|
2973
|
+
"describes a state that **a single public event could invalidate",
|
|
2974
|
+
"within the document's expected refresh cadence**. Five recurring",
|
|
2975
|
+
"categories cover the common cases:",
|
|
2976
|
+
"",
|
|
2977
|
+
"| Category | Examples |",
|
|
2978
|
+
"|----------|----------|",
|
|
2979
|
+
'| **Ownership and investors** | "co-owned by X, Y, Z"; "majority-owned by"; "minority stake held by"; cap table; current investor lists |',
|
|
2980
|
+
'| **Leadership tenure** | "X is CEO"; "X chairs the committee"; "X serves as"; current board chair; current executive team |',
|
|
2981
|
+
'| **Regulatory or rule status** | "is in force"; "under public consultation"; "pending finalization"; "currently enjoined"; "under active investigation" |',
|
|
2982
|
+
'| **Litigation and enforcement** | "is under active scrutiny"; "investigation is ongoing"; "appeal is pending"; "consent decree remains in effect" |',
|
|
2983
|
+
'| **Pricing, valuation, and metrics** | "valued at $XB"; "annual revenue ~$X"; "processes N transactions"; "serves N customers" \u2014 when the figure is sourced from a dated press release or filing |',
|
|
2984
|
+
"",
|
|
2985
|
+
"A claim is **not** time-sensitive (and does not need the qualifier)",
|
|
2986
|
+
"when it describes a historical event, a statutory text, or a",
|
|
2987
|
+
"structural fact that does not change without amendment. Examples",
|
|
2988
|
+
"that are safe without a qualifier:",
|
|
2989
|
+
"",
|
|
2990
|
+
'- "Acquired by Advent International in May 2019" (a dated historical event \u2014 the date itself is the anchor)',
|
|
2991
|
+
'- "21 CFR Part 11 establishes criteria for electronic records" (statutory text)',
|
|
2992
|
+
'- "Headquartered in Dallas, Texas" (changes only on a documented move)',
|
|
2993
|
+
'- "Founded in 1999" (a one-time event)',
|
|
2994
|
+
"",
|
|
2995
|
+
"## Format",
|
|
2996
|
+
"",
|
|
2997
|
+
"Two acceptable forms:",
|
|
2998
|
+
"",
|
|
2999
|
+
"```markdown",
|
|
3000
|
+
"As of May 2026, Acme is owned by ...",
|
|
3001
|
+
"... Acme's CEO (as of May 2026) is ...",
|
|
3002
|
+
"```",
|
|
3003
|
+
"",
|
|
3004
|
+
"Use the granularity that matches the source citation:",
|
|
3005
|
+
"",
|
|
3006
|
+
"- `as of YYYY-MM-DD` when the source is a specific filing, press release, or court order with a precise date.",
|
|
3007
|
+
"- `as of Month YYYY` when the source is a self-reported page (e.g., a company's `About` page) where only the month-of-snapshot is meaningful.",
|
|
3008
|
+
"- Do **not** write `as of 2026` (year-only) \u2014 that's too coarse to drive a refresh decision.",
|
|
3009
|
+
"",
|
|
3010
|
+
"## Examples",
|
|
3011
|
+
"",
|
|
3012
|
+
"### Wrong \u2014 present-tense framing of a state that can change",
|
|
3013
|
+
"",
|
|
3014
|
+
"```markdown",
|
|
3015
|
+
"Availity is co-owned by major Blue Cross Blue Shield plans",
|
|
3016
|
+
"(HCSC, Elevance Health/Anthem, BCBS Minnesota), Francisco",
|
|
3017
|
+
"Partners, Novo Holdings, and Prettybrook Partners.",
|
|
3018
|
+
"```",
|
|
3019
|
+
"",
|
|
3020
|
+
"This claim might have been true at a specific date but goes stale",
|
|
3021
|
+
"as soon as any one investor exits. A refresh agent has no",
|
|
3022
|
+
"mechanical signal that the claim is time-sensitive.",
|
|
3023
|
+
"",
|
|
3024
|
+
"### Right \u2014 explicit qualifier on the first claim",
|
|
3025
|
+
"",
|
|
3026
|
+
"```markdown",
|
|
3027
|
+
"As of May 2026, Availity is co-owned by a mix of health plan",
|
|
3028
|
+
"strategic investors (HCSC, Elevance Health/Anthem, BCBS",
|
|
3029
|
+
"Minnesota, Humana, GuideWell) and financial sponsors (Novo",
|
|
3030
|
+
"Holdings, Prettybrook Partners).",
|
|
3031
|
+
"```",
|
|
3032
|
+
"",
|
|
3033
|
+
"The `as of May 2026` anchors the document's snapshot for the",
|
|
3034
|
+
"ownership claim. Subsequent references to Availity's ownership in",
|
|
3035
|
+
"the same document inherit that snapshot date.",
|
|
3036
|
+
"",
|
|
3037
|
+
"## When the convention applies",
|
|
3038
|
+
"",
|
|
3039
|
+
"The rule applies to every Markdown document any agent writes under",
|
|
3040
|
+
"the following path globs:",
|
|
3041
|
+
"",
|
|
3042
|
+
...tf.paths.map((p) => `- \`${p}\``),
|
|
3043
|
+
"",
|
|
3044
|
+
"It does **not** apply to:",
|
|
3045
|
+
"",
|
|
3046
|
+
"- Meeting notes (`meetings/**`) \u2014 the meeting date itself is the implicit `as of` anchor.",
|
|
3047
|
+
"- Requirement documents (`requirements/**`) \u2014 formal requirements are versioned and dated via their own frontmatter.",
|
|
3048
|
+
"- The project context page (`project-context.md`) \u2014 maintained as a living snapshot under direct human review.",
|
|
3049
|
+
"",
|
|
3050
|
+
"## Refresh-agent behaviour",
|
|
3051
|
+
"",
|
|
3052
|
+
"A refresh pass on any covered document MUST:",
|
|
3053
|
+
"",
|
|
3054
|
+
"1. Grep for `as of ` (case-insensitive) in the document.",
|
|
3055
|
+
"2. For each qualifier, compute `today - <qualifier-date>` in days.",
|
|
3056
|
+
"3. Classify each qualified claim against the category-specific",
|
|
3057
|
+
" cadence table below.",
|
|
3058
|
+
"4. Re-verify every claim whose qualifier is older than its cadence.",
|
|
3059
|
+
"5. Update the qualifier to today's date on every re-verified claim",
|
|
3060
|
+
" that remains accurate.",
|
|
3061
|
+
"6. Treat documents that omit qualifiers entirely as needing",
|
|
3062
|
+
" **back-fill** before any other verification is attempted \u2014 the",
|
|
3063
|
+
" absence of qualifiers is itself a refresh finding.",
|
|
3064
|
+
"",
|
|
3065
|
+
"### Refresh cadence table",
|
|
3066
|
+
"",
|
|
3067
|
+
"Each category carries its own refresh cadence in days. Faster-decay",
|
|
3068
|
+
"claims (regulatory status, litigation) refresh more often;",
|
|
3069
|
+
"slower-decay claims (ownership, dated metrics) refresh less often.",
|
|
3070
|
+
"",
|
|
3071
|
+
"| Category | Cadence (days) |",
|
|
3072
|
+
"|----------|----------------|",
|
|
3073
|
+
`| \`ownership\` | ${tf.cadences.ownership} |`,
|
|
3074
|
+
`| \`company-leadership\` | ${tf.cadences["company-leadership"]} |`,
|
|
3075
|
+
`| \`regulatory-status\` | ${tf.cadences["regulatory-status"]} |`,
|
|
3076
|
+
`| \`litigation\` | ${tf.cadences.litigation} |`,
|
|
3077
|
+
`| \`dated-metrics\` | ${tf.cadences["dated-metrics"]} |`,
|
|
3078
|
+
"",
|
|
3079
|
+
"Consumers may override any subset of cadences via",
|
|
3080
|
+
"`AgentConfigOptions.temporalFraming.cadences`. Unspecified",
|
|
3081
|
+
"categories fall through to the defaults above."
|
|
3082
|
+
];
|
|
3083
|
+
if (tf.emitChecker) {
|
|
3084
|
+
lines.push(
|
|
3085
|
+
"",
|
|
3086
|
+
"## Lint",
|
|
3087
|
+
"",
|
|
3088
|
+
"The bundled `.claude/procedures/check-temporal-framing.sh` helper",
|
|
3089
|
+
"enforces the rule mechanically. Given a set of changed files (as",
|
|
3090
|
+
"positional arguments or a newline-separated list on stdin), it",
|
|
3091
|
+
"fails non-zero when any file matches a configured path glob and",
|
|
3092
|
+
"carries time-sensitive framing without an `as of ` qualifier.",
|
|
3093
|
+
"",
|
|
3094
|
+
"```bash",
|
|
3095
|
+
"# Pre-commit (staged files):",
|
|
3096
|
+
"git diff --cached --name-only \\",
|
|
3097
|
+
" | .claude/procedures/check-temporal-framing.sh",
|
|
3098
|
+
"",
|
|
3099
|
+
"# CI (branch-vs-base):",
|
|
3100
|
+
"git diff --name-only origin/main...HEAD \\",
|
|
3101
|
+
" | .claude/procedures/check-temporal-framing.sh",
|
|
3102
|
+
"```",
|
|
3103
|
+
"",
|
|
3104
|
+
"The lint is a coarse signal \u2014 it flags files that look",
|
|
3105
|
+
"time-sensitive but carry no qualifier. Authors and reviewers are",
|
|
3106
|
+
"still expected to validate that each qualifier is correctly",
|
|
3107
|
+
"anchored to its source citation."
|
|
3108
|
+
);
|
|
3109
|
+
}
|
|
3110
|
+
lines.push(
|
|
3111
|
+
"",
|
|
3112
|
+
"## Out of scope",
|
|
3113
|
+
"",
|
|
3114
|
+
"- Auto-generating qualifiers from source-citation dates. Authors",
|
|
3115
|
+
" read the source and write the qualifier explicitly.",
|
|
3116
|
+
"- Replacing existing dated frontmatter conventions. Frontmatter",
|
|
3117
|
+
" `date:` fields anchor the document's overall snapshot; the",
|
|
3118
|
+
" inline qualifier anchors individual time-sensitive claims so a",
|
|
3119
|
+
" refresh pass can re-verify them without re-reading the whole",
|
|
3120
|
+
" document.",
|
|
3121
|
+
"- Back-filling pre-existing documents in bulk. Refresh passes",
|
|
3122
|
+
" back-fill qualifiers on the documents they touch; bulk",
|
|
3123
|
+
" back-fill is a downstream consumer cleanup task, not a",
|
|
3124
|
+
" configulator change."
|
|
3125
|
+
);
|
|
3126
|
+
return lines.join("\n");
|
|
3127
|
+
}
|
|
3128
|
+
function renderTemporalFramingCheckerScript(tf) {
|
|
3129
|
+
const patternsLiteral = tf.paths.map((p) => ` ${JSON.stringify(p)}`).join("\n");
|
|
3130
|
+
return [
|
|
3131
|
+
"#!/usr/bin/env bash",
|
|
3132
|
+
"# check-temporal-framing.sh \u2014 Enforce the temporal-framing convention.",
|
|
3133
|
+
"#",
|
|
3134
|
+
"# Usage:",
|
|
3135
|
+
"# .claude/procedures/check-temporal-framing.sh <file>...",
|
|
3136
|
+
"# git diff --name-only HEAD | .claude/procedures/check-temporal-framing.sh",
|
|
3137
|
+
"#",
|
|
3138
|
+
"# Fails non-zero when any changed file matches a configured path",
|
|
3139
|
+
"# pattern AND contains present-tense framing of a time-sensitive",
|
|
3140
|
+
"# claim category AND does NOT carry an `as of ` qualifier anywhere",
|
|
3141
|
+
"# in the file. The lint is file-level, not line-level \u2014 the cheap",
|
|
3142
|
+
"# coarse signal is enough to catch the failure mode the convention",
|
|
3143
|
+
"# targets, and a finer-grained check would carry too much false",
|
|
3144
|
+
"# positive cost.",
|
|
3145
|
+
"#",
|
|
3146
|
+
"# The path patterns are rendered from the consumer's",
|
|
3147
|
+
"# TemporalFramingConfig at synth time \u2014 do not edit by hand;",
|
|
3148
|
+
"# regenerate via `pnpm exec projen`.",
|
|
3149
|
+
"#",
|
|
3150
|
+
"# Patterns support the `**` recursive-segment wildcard even though",
|
|
3151
|
+
"# bash's native `[[ $file == $glob ]]` pattern matching does not:",
|
|
3152
|
+
"# each glob is converted to a POSIX-extended regex internally and",
|
|
3153
|
+
"# matched with `=~`, so the script works uniformly on bash 3.2",
|
|
3154
|
+
"# (macOS default) and bash 4+ (Linux / CI).",
|
|
3155
|
+
"",
|
|
3156
|
+
"set -uo pipefail",
|
|
3157
|
+
"",
|
|
3158
|
+
"err() {",
|
|
3159
|
+
' printf "check-temporal-framing.sh: %s\\n" "$*" >&2',
|
|
3160
|
+
"}",
|
|
3161
|
+
"",
|
|
3162
|
+
"# Path glob patterns. Translated to anchored POSIX-extended regexes",
|
|
3163
|
+
"# at runtime by `glob_to_regex` below so `**` matches recursively",
|
|
3164
|
+
"# across path segments.",
|
|
3165
|
+
"path_patterns=(",
|
|
3166
|
+
patternsLiteral,
|
|
3167
|
+
")",
|
|
3168
|
+
"",
|
|
3169
|
+
"# Triggers \u2014 present-tense framing keywords that indicate a",
|
|
3170
|
+
"# time-sensitive claim category. Match is case-insensitive and",
|
|
3171
|
+
"# whole-word; the keyword list is deliberately small (one or two",
|
|
3172
|
+
"# canonical phrases per category) to keep false positives low.",
|
|
3173
|
+
"triggers=(",
|
|
3174
|
+
' "is owned by"',
|
|
3175
|
+
' "co-owned by"',
|
|
3176
|
+
' "majority-owned by"',
|
|
3177
|
+
' "minority-owned by"',
|
|
3178
|
+
' "is co-owned"',
|
|
3179
|
+
' "is currently"',
|
|
3180
|
+
' "currently chairs"',
|
|
3181
|
+
' "currently serves"',
|
|
3182
|
+
' "is in force"',
|
|
3183
|
+
' "under public consultation"',
|
|
3184
|
+
' "under active investigation"',
|
|
3185
|
+
' "is pending"',
|
|
3186
|
+
' "investigation is ongoing"',
|
|
3187
|
+
")",
|
|
3188
|
+
"",
|
|
3189
|
+
"# Convert a bash-style glob into an anchored POSIX-extended regex:",
|
|
3190
|
+
"# **/ \u2192 (.+/)? (zero or more path segments plus trailing slash)",
|
|
3191
|
+
"# ** \u2192 .* (anywhere, including `/`)",
|
|
3192
|
+
"# * \u2192 [^/]* (single path segment)",
|
|
3193
|
+
"# ? \u2192 [^/] (single non-slash char)",
|
|
3194
|
+
"glob_to_regex() {",
|
|
3195
|
+
' local glob="$1"',
|
|
3196
|
+
' local regex=""',
|
|
3197
|
+
" local i=0",
|
|
3198
|
+
" local len=${#glob}",
|
|
3199
|
+
" while (( i < len )); do",
|
|
3200
|
+
' local c="${glob:i:1}"',
|
|
3201
|
+
' if [[ "$c" == "*" ]]; then',
|
|
3202
|
+
' local next="${glob:i+1:1}"',
|
|
3203
|
+
' if [[ "$next" == "*" ]]; then',
|
|
3204
|
+
' local after="${glob:i+2:1}"',
|
|
3205
|
+
' if [[ "$after" == "/" ]]; then',
|
|
3206
|
+
' regex+="(.+/)?"',
|
|
3207
|
+
" i=$((i+3))",
|
|
3208
|
+
" continue",
|
|
3209
|
+
" fi",
|
|
3210
|
+
' regex+=".*"',
|
|
3211
|
+
" i=$((i+2))",
|
|
3212
|
+
" continue",
|
|
3213
|
+
" fi",
|
|
3214
|
+
' regex+="[^/]*"',
|
|
3215
|
+
" i=$((i+1))",
|
|
3216
|
+
" continue",
|
|
3217
|
+
" fi",
|
|
3218
|
+
' if [[ "$c" == "?" ]]; then',
|
|
3219
|
+
' regex+="[^/]"',
|
|
3220
|
+
" i=$((i+1))",
|
|
3221
|
+
" continue",
|
|
3222
|
+
" fi",
|
|
3223
|
+
' case "$c" in',
|
|
3224
|
+
" .|+|\\(|\\)|\\[|\\]|\\{|\\}|\\||\\^|\\$|\\\\)",
|
|
3225
|
+
' regex+="\\\\$c"',
|
|
3226
|
+
" ;;",
|
|
3227
|
+
" *)",
|
|
3228
|
+
' regex+="$c"',
|
|
3229
|
+
" ;;",
|
|
3230
|
+
" esac",
|
|
3231
|
+
" i=$((i+1))",
|
|
3232
|
+
" done",
|
|
3233
|
+
' printf "^%s$" "$regex"',
|
|
3234
|
+
"}",
|
|
3235
|
+
"",
|
|
3236
|
+
"# Check whether $file matches any of the configured path patterns.",
|
|
3237
|
+
"# Returns 0 on match, 1 otherwise.",
|
|
3238
|
+
"matches_any_pattern() {",
|
|
3239
|
+
' local file="$1"',
|
|
3240
|
+
" local pattern regex",
|
|
3241
|
+
' for pattern in "${path_patterns[@]}"; do',
|
|
3242
|
+
' regex=$(glob_to_regex "$pattern")',
|
|
3243
|
+
' if [[ "$file" =~ $regex ]]; then',
|
|
3244
|
+
" return 0",
|
|
3245
|
+
" fi",
|
|
3246
|
+
" done",
|
|
3247
|
+
" return 1",
|
|
3248
|
+
"}",
|
|
3249
|
+
"",
|
|
3250
|
+
"# Check whether $file contains any present-tense framing trigger",
|
|
3251
|
+
"# without an `as of ` qualifier anywhere in the file. Returns 0",
|
|
3252
|
+
"# when the file is OK (no triggers, or triggers + qualifier), 1",
|
|
3253
|
+
"# when the file is malformed (triggers + no qualifier).",
|
|
3254
|
+
"file_ok() {",
|
|
3255
|
+
' local file="$1"',
|
|
3256
|
+
' [[ ! -f "$file" ]] && return 0',
|
|
3257
|
+
" local found_trigger=0",
|
|
3258
|
+
" local trigger",
|
|
3259
|
+
' for trigger in "${triggers[@]}"; do',
|
|
3260
|
+
' if grep -qi -- "$trigger" "$file"; then',
|
|
3261
|
+
" found_trigger=1",
|
|
3262
|
+
" break",
|
|
3263
|
+
" fi",
|
|
3264
|
+
" done",
|
|
3265
|
+
" if [[ $found_trigger -eq 0 ]]; then",
|
|
3266
|
+
" return 0",
|
|
3267
|
+
" fi",
|
|
3268
|
+
' if grep -qi -- "as of " "$file"; then',
|
|
3269
|
+
" return 0",
|
|
3270
|
+
" fi",
|
|
3271
|
+
" return 1",
|
|
3272
|
+
"}",
|
|
3273
|
+
"",
|
|
3274
|
+
"# Read the file list from positional args or stdin.",
|
|
3275
|
+
"files=()",
|
|
3276
|
+
"if [[ $# -gt 0 ]]; then",
|
|
3277
|
+
' files=("$@")',
|
|
3278
|
+
"else",
|
|
3279
|
+
" while IFS= read -r line; do",
|
|
3280
|
+
' [[ -n "$line" ]] && files+=("$line")',
|
|
3281
|
+
" done",
|
|
3282
|
+
"fi",
|
|
3283
|
+
"",
|
|
3284
|
+
"if [[ ${#files[@]} -eq 0 ]]; then",
|
|
3285
|
+
" exit 0",
|
|
3286
|
+
"fi",
|
|
3287
|
+
"",
|
|
3288
|
+
"violations=0",
|
|
3289
|
+
'for file in "${files[@]}"; do',
|
|
3290
|
+
' if ! matches_any_pattern "$file"; then',
|
|
3291
|
+
" continue",
|
|
3292
|
+
" fi",
|
|
3293
|
+
' if ! file_ok "$file"; then',
|
|
3294
|
+
' err "$file: contains time-sensitive framing without an \\"as of\\" qualifier"',
|
|
3295
|
+
" violations=$((violations+1))",
|
|
3296
|
+
" fi",
|
|
3297
|
+
"done",
|
|
3298
|
+
"",
|
|
3299
|
+
"if [[ $violations -gt 0 ]]; then",
|
|
3300
|
+
' err "found $violations file(s) violating the temporal-framing convention"',
|
|
3301
|
+
" exit 1",
|
|
3302
|
+
"fi",
|
|
3303
|
+
"exit 0"
|
|
3304
|
+
].join("\n");
|
|
3305
|
+
}
|
|
3306
|
+
function assertValidPaths(paths) {
|
|
3307
|
+
if (!Array.isArray(paths)) {
|
|
3308
|
+
throw new Error(
|
|
3309
|
+
`TemporalFramingConfig.paths must be an array; got ${typeof paths}`
|
|
3310
|
+
);
|
|
3311
|
+
}
|
|
3312
|
+
for (let i = 0; i < paths.length; i++) {
|
|
3313
|
+
const entry = paths[i];
|
|
3314
|
+
if (typeof entry !== "string" || entry.trim().length === 0) {
|
|
3315
|
+
throw new Error(
|
|
3316
|
+
`TemporalFramingConfig.paths[${i}] must be a non-empty string; got ${JSON.stringify(
|
|
3317
|
+
entry
|
|
3318
|
+
)}`
|
|
3319
|
+
);
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
function resolveCadences(supplied) {
|
|
3324
|
+
const merged = {
|
|
3325
|
+
...DEFAULT_TEMPORAL_FRAMING_CADENCES
|
|
3326
|
+
};
|
|
3327
|
+
if (supplied !== void 0) {
|
|
3328
|
+
for (const category of TEMPORAL_FRAMING_CATEGORY_VALUES) {
|
|
3329
|
+
const value = supplied[category];
|
|
3330
|
+
if (value === void 0) {
|
|
3331
|
+
continue;
|
|
3332
|
+
}
|
|
3333
|
+
assertValidCadence(value, category);
|
|
3334
|
+
merged[category] = value;
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
return merged;
|
|
3338
|
+
}
|
|
3339
|
+
function assertValidCadence(value, field) {
|
|
3340
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
3341
|
+
throw new Error(
|
|
3342
|
+
`TemporalFramingConfig.cadences.${field} must be a positive integer; got ${value}`
|
|
3343
|
+
);
|
|
3344
|
+
}
|
|
3345
|
+
if (value <= 0) {
|
|
3346
|
+
throw new Error(
|
|
3347
|
+
`TemporalFramingConfig.cadences.${field} must be a positive integer; got ${value}`
|
|
3348
|
+
);
|
|
3349
|
+
}
|
|
3350
|
+
}
|
|
3351
|
+
|
|
2901
3352
|
// src/agent/bundles/base.ts
|
|
2902
3353
|
var createPackageSkill = {
|
|
2903
3354
|
name: "create-package",
|
|
@@ -4007,6 +4458,16 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
4007
4458
|
},
|
|
4008
4459
|
tags: ["workflow"]
|
|
4009
4460
|
},
|
|
4461
|
+
{
|
|
4462
|
+
name: "temporal-framing-convention",
|
|
4463
|
+
description: "Temporal-framing convention: every agent-authored time-sensitive factual claim (ownership, leadership tenure, regulatory status, litigation, dated metrics) carries an inline `as of [YYYY-MM-DD]` or `as of [Month YYYY]` qualifier on first occurrence so refresh agents have a mechanical signal for which claims to re-verify.",
|
|
4464
|
+
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
4465
|
+
content: renderTemporalFramingRuleContent(resolveTemporalFraming()),
|
|
4466
|
+
platforms: {
|
|
4467
|
+
cursor: { exclude: true }
|
|
4468
|
+
},
|
|
4469
|
+
tags: ["workflow"]
|
|
4470
|
+
},
|
|
4010
4471
|
{
|
|
4011
4472
|
name: "skill-evals",
|
|
4012
4473
|
description: "Skill eval harness contract: declarative prompt/expected-output regression suites per skill at `<skillsRoot>/<skill-name>/evals/evals.json`, parameterised by a shared product-context fixture so the same eval shape works across every configulator-consuming project without forking fixtures.",
|
|
@@ -6250,19 +6711,29 @@ function buildCompanyProfileAnalystSubAgent(paths, issueDefaults, tier) {
|
|
|
6250
6711
|
" - <source URL> \u2014 <date accessed>",
|
|
6251
6712
|
" ```",
|
|
6252
6713
|
"",
|
|
6253
|
-
"4. **
|
|
6714
|
+
"4. **Apply the `temporal-framing-convention` rule.** Every",
|
|
6715
|
+
" time-sensitive factual claim in the profile \u2014 ownership,",
|
|
6716
|
+
" leadership tenure, regulatory status, litigation, dated metrics",
|
|
6717
|
+
" \u2014 must carry an inline `as of [YYYY-MM-DD]` or `as of [Month",
|
|
6718
|
+
" YYYY]` qualifier on its **first** occurrence in the document.",
|
|
6719
|
+
" Use the granularity that matches the source citation (precise",
|
|
6720
|
+
" date for a filing or press release; month for a self-reported",
|
|
6721
|
+
" `About` page). The qualifier is mechanical, not editorial \u2014 it",
|
|
6722
|
+
" is the signal Phase 5 (Refresh) uses to grep for stale claims.",
|
|
6723
|
+
"",
|
|
6724
|
+
"5. **Decide whether a follow-up issue is warranted.** Create a",
|
|
6254
6725
|
" `company:followup` issue (depending on this draft issue) only if",
|
|
6255
6726
|
" the profile lists at least one follow-up candidate. Otherwise,",
|
|
6256
6727
|
" note in the draft issue's closing comment that no follow-up is",
|
|
6257
6728
|
" needed.",
|
|
6258
6729
|
"",
|
|
6259
|
-
"
|
|
6730
|
+
"6. **File a `company:match` issue** (depending on this draft issue)",
|
|
6260
6731
|
" so the profile gets enriched against the project's business-model",
|
|
6261
6732
|
" canvases. Skip the match issue only when the invoking project has",
|
|
6262
6733
|
" no business-model canvases at all (Phase 4 will detect and exit",
|
|
6263
6734
|
" gracefully in that case).",
|
|
6264
6735
|
"",
|
|
6265
|
-
"
|
|
6736
|
+
"7. **Commit and push** the profile file. Close the draft issue.",
|
|
6266
6737
|
"",
|
|
6267
6738
|
"---",
|
|
6268
6739
|
"",
|
|
@@ -6501,26 +6972,51 @@ function buildCompanyProfileAnalystSubAgent(paths, issueDefaults, tier) {
|
|
|
6501
6972
|
" the issue body does not set `force: true`, close the issue with",
|
|
6502
6973
|
" a short comment and stop \u2014 do not burn the search budget.",
|
|
6503
6974
|
"",
|
|
6504
|
-
"3. **
|
|
6975
|
+
"3. **Grep for `as of ` qualifiers.** Per the",
|
|
6976
|
+
" `temporal-framing-convention` rule, every time-sensitive claim in",
|
|
6977
|
+
" the profile carries an inline `as of [YYYY-MM-DD]` or `as of",
|
|
6978
|
+
" [Month YYYY]` qualifier. Build the list of qualified claims and",
|
|
6979
|
+
" compute the age (in days) of each one against today's date.",
|
|
6980
|
+
" Classify each claim against the cadence table in the",
|
|
6981
|
+
" `temporal-framing-convention` rule:",
|
|
6982
|
+
" - **company-leadership** \u2014 90 days",
|
|
6983
|
+
" - **regulatory-status** \u2014 30 days",
|
|
6984
|
+
" - **litigation** \u2014 30 days",
|
|
6985
|
+
" - **ownership** \u2014 180 days",
|
|
6986
|
+
" - **dated-metrics** \u2014 180 days",
|
|
6987
|
+
"",
|
|
6988
|
+
" Treat every qualifier older than its category's cadence as a",
|
|
6989
|
+
" re-verification target for the targeted-search step below. If the",
|
|
6990
|
+
" profile contains **no** `as of ` qualifiers at all, treat the",
|
|
6991
|
+
" entire profile as needing **back-fill** \u2014 apply the convention to",
|
|
6992
|
+
" every time-sensitive claim before running the targeted searches.",
|
|
6993
|
+
"",
|
|
6994
|
+
"4. **Run 4\u20136 targeted searches.** Focus on the categories surfaced by",
|
|
6995
|
+
" the qualifier grep (step 3), plus any additional signals worth a",
|
|
6996
|
+
" refresh:",
|
|
6505
6997
|
" - `<company name>` + recent news, press releases, or blog posts",
|
|
6506
6998
|
" - Product launches, feature changes, or pricing updates",
|
|
6507
6999
|
" - Funding rounds, acquisitions, or material ownership changes",
|
|
6508
7000
|
" - Leadership changes (CEO, CTO, CPO, notable departures/arrivals)",
|
|
6509
7001
|
" - New partnerships, certifications, or regulatory milestones",
|
|
6510
7002
|
"",
|
|
6511
|
-
"
|
|
7003
|
+
"5. **Update the profile in place.** Edit the affected sections with",
|
|
6512
7004
|
" the new information. Cite every new claim. Preserve the slug and",
|
|
6513
7005
|
" the original `parent_issue` field. Bump the `date` frontmatter to",
|
|
6514
|
-
" today's date.",
|
|
7006
|
+
" today's date. **Update the `as of` qualifier** on every claim you",
|
|
7007
|
+
" re-verified \u2014 change the date to today's date when the claim",
|
|
7008
|
+
" still holds; rewrite the framing when the claim has changed.",
|
|
7009
|
+
" Surface unverifiable or stale-and-changed claims through",
|
|
7010
|
+
" `## Risks / Open Questions` rather than silently dropping them.",
|
|
6515
7011
|
"",
|
|
6516
|
-
"
|
|
7012
|
+
"6. **Do not silently re-type the company.** If the refresh surfaces",
|
|
6517
7013
|
" evidence that the company's primary type has changed (e.g., an",
|
|
6518
7014
|
" industry-player that now sells a directly competing product),",
|
|
6519
7015
|
" flag it in `## Risks / Open Questions` and stop \u2014 do **not**",
|
|
6520
7016
|
" rewrite the `company_type` frontmatter without an explicit",
|
|
6521
7017
|
" override in the refresh issue body (`retype: <new type>`).",
|
|
6522
7018
|
"",
|
|
6523
|
-
"
|
|
7019
|
+
"7. **Append a revision-history row.** Summarize the delta in one",
|
|
6524
7020
|
" line:",
|
|
6525
7021
|
"",
|
|
6526
7022
|
" ```markdown",
|
|
@@ -6535,14 +7031,14 @@ function buildCompanyProfileAnalystSubAgent(paths, issueDefaults, tier) {
|
|
|
6535
7031
|
" | YYYY-MM-DD | Refreshed: no material change |",
|
|
6536
7032
|
" ```",
|
|
6537
7033
|
"",
|
|
6538
|
-
"
|
|
7034
|
+
"8. **Update reference entries** (if the project tracks company",
|
|
6539
7035
|
" references under research directories). If the company's",
|
|
6540
7036
|
" relevance to a research area has shifted \u2014 for example, it is no",
|
|
6541
7037
|
" longer a live competitor, or it now plays in a new segment \u2014",
|
|
6542
7038
|
" update the corresponding entry under `referencedIn` and the",
|
|
6543
7039
|
" research-area doc that links to this profile.",
|
|
6544
7040
|
"",
|
|
6545
|
-
"
|
|
7041
|
+
"9. **Commit and push.** Close the refresh issue with a short comment",
|
|
6546
7042
|
" summarizing the delta (or `no material change`).",
|
|
6547
7043
|
"",
|
|
6548
7044
|
"---",
|
|
@@ -15471,6 +15967,17 @@ function buildPeopleProfileAnalystSubAgent(paths, issueDefaults, tier) {
|
|
|
15471
15967
|
"full background, and do not rewrite the profile body for facts that",
|
|
15472
15968
|
"have not changed.",
|
|
15473
15969
|
"",
|
|
15970
|
+
"**Temporal framing.** Refreshes follow the same grep-`as of `-and-",
|
|
15971
|
+
"re-verify pattern documented for `company:refresh` in the",
|
|
15972
|
+
"`company-profile-analyst` bundle (Phase 5, steps 3 and 5): build",
|
|
15973
|
+
"the list of qualified claims, classify each against the cadence",
|
|
15974
|
+
"table in the `temporal-framing-convention` rule (90d for current",
|
|
15975
|
+
"role / employer / tenure, falling back to 180d for slower-decay",
|
|
15976
|
+
"claims), re-verify every claim past cadence, and update the",
|
|
15977
|
+
"qualifier date on every claim that still holds. Treat a profile",
|
|
15978
|
+
"with no `as of ` qualifiers as needing back-fill before any other",
|
|
15979
|
+
"verification.",
|
|
15980
|
+
"",
|
|
15474
15981
|
"### Steps",
|
|
15475
15982
|
"",
|
|
15476
15983
|
"1. **Read the existing profile** at the path referenced in the issue",
|
|
@@ -29173,6 +29680,17 @@ var AgentConfig = class _AgentConfig extends Component8 {
|
|
|
29173
29680
|
});
|
|
29174
29681
|
}
|
|
29175
29682
|
}
|
|
29683
|
+
const resolvedTemporalFraming = validateTemporalFramingConfig(
|
|
29684
|
+
this.options.temporalFraming
|
|
29685
|
+
);
|
|
29686
|
+
if (resolvedTemporalFraming.enabled && resolvedTemporalFraming.emitChecker) {
|
|
29687
|
+
new TextFile4(this, ".claude/procedures/check-temporal-framing.sh", {
|
|
29688
|
+
lines: renderTemporalFramingCheckerScript(
|
|
29689
|
+
resolvedTemporalFraming
|
|
29690
|
+
).split("\n"),
|
|
29691
|
+
executable: true
|
|
29692
|
+
});
|
|
29693
|
+
}
|
|
29176
29694
|
const platforms = this.resolvePlatforms();
|
|
29177
29695
|
const rules = this.resolveRules();
|
|
29178
29696
|
const skills = this.resolveSkills();
|
|
@@ -29520,6 +30038,23 @@ ${hook}`
|
|
|
29520
30038
|
});
|
|
29521
30039
|
}
|
|
29522
30040
|
}
|
|
30041
|
+
const resolvedTemporalFramingForRules = resolveTemporalFraming(
|
|
30042
|
+
this.options.temporalFraming
|
|
30043
|
+
);
|
|
30044
|
+
if (this.options.temporalFraming) {
|
|
30045
|
+
const temporalFramingRule = ruleMap.get("temporal-framing-convention");
|
|
30046
|
+
if (temporalFramingRule) {
|
|
30047
|
+
const temporalFramingContent = renderTemporalFramingRuleContent(
|
|
30048
|
+
resolvedTemporalFramingForRules
|
|
30049
|
+
);
|
|
30050
|
+
if (temporalFramingContent !== temporalFramingRule.content) {
|
|
30051
|
+
ruleMap.set("temporal-framing-convention", {
|
|
30052
|
+
...temporalFramingRule,
|
|
30053
|
+
content: temporalFramingContent
|
|
30054
|
+
});
|
|
30055
|
+
}
|
|
30056
|
+
}
|
|
30057
|
+
}
|
|
29523
30058
|
const tierExamples = this.options.features?.sourceTierExamples;
|
|
29524
30059
|
if (_AgentConfig.hasActiveTierExamples(tierExamples)) {
|
|
29525
30060
|
const sourceRule = ruleMap.get("source-quality-verification");
|
|
@@ -34300,6 +34835,10 @@ export {
|
|
|
34300
34835
|
DEFAULT_STATE_FILE_PATH,
|
|
34301
34836
|
DEFAULT_STATUS_LABELS,
|
|
34302
34837
|
DEFAULT_TEARDOWN_BRANCH_PATTERNS,
|
|
34838
|
+
DEFAULT_TEMPORAL_FRAMING_CADENCES,
|
|
34839
|
+
DEFAULT_TEMPORAL_FRAMING_EMIT_CHECKER,
|
|
34840
|
+
DEFAULT_TEMPORAL_FRAMING_ENABLED,
|
|
34841
|
+
DEFAULT_TEMPORAL_FRAMING_PATHS,
|
|
34303
34842
|
DEFAULT_TYPE_LABELS,
|
|
34304
34843
|
DEFAULT_UNBLOCK_COMMENT_TEMPLATE,
|
|
34305
34844
|
DEFAULT_UNBLOCK_DEPENDENTS_ENABLED,
|
|
@@ -34331,6 +34870,7 @@ export {
|
|
|
34331
34870
|
SUPPRESSED_WORKFLOW_RULE_NAMES,
|
|
34332
34871
|
SampleLang,
|
|
34333
34872
|
StarlightProject,
|
|
34873
|
+
TEMPORAL_FRAMING_CATEGORY_VALUES,
|
|
34334
34874
|
TIER_AWARE_BUNDLE_NAMES,
|
|
34335
34875
|
TestRunner,
|
|
34336
34876
|
TsDocCoverageKind,
|
|
@@ -34454,6 +34994,8 @@ export {
|
|
|
34454
34994
|
renderSkillEvalsRunnerScript,
|
|
34455
34995
|
renderSourceTierExamples,
|
|
34456
34996
|
renderStubIndexConventionRuleContent,
|
|
34997
|
+
renderTemporalFramingCheckerScript,
|
|
34998
|
+
renderTemporalFramingRuleContent,
|
|
34457
34999
|
renderUnblockDependentsScript,
|
|
34458
35000
|
renderUnblockDependentsSection,
|
|
34459
35001
|
requirementsAnalystBundle,
|
|
@@ -34479,6 +35021,7 @@ export {
|
|
|
34479
35021
|
resolveSharedEditing,
|
|
34480
35022
|
resolveSkillEvals,
|
|
34481
35023
|
resolveTemplateVariables,
|
|
35024
|
+
resolveTemporalFraming,
|
|
34482
35025
|
resolveTypeScriptProjectOutdir,
|
|
34483
35026
|
resolveUnblockDependents,
|
|
34484
35027
|
runScan,
|
|
@@ -34500,6 +35043,7 @@ export {
|
|
|
34500
35043
|
validateSharedEditingConfig,
|
|
34501
35044
|
validateSkillEvalsConfig,
|
|
34502
35045
|
validateStarlightSingleton,
|
|
35046
|
+
validateTemporalFramingConfig,
|
|
34503
35047
|
validateUnblockDependentsConfig,
|
|
34504
35048
|
vitestBundle
|
|
34505
35049
|
};
|