frontend-project-context 1.8.0 → 1.10.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 +29 -1
- package/README.md +26 -7
- package/UPGRADING.md +24 -0
- package/docs/00-PRODUCT-CONSTITUTION.md +44 -12
- package/docs/08-INSTALLATION-AND-DISTRIBUTION.md +9 -3
- package/docs/14-FORMAL-RELEASE-READINESS.md +14 -0
- package/docs/28-REAL-PROJECT-ONBOARDING-CLOSURE-DESIGN.md +10 -0
- package/docs/29-REAL-PROJECT-1.8.0-INITIALIZATION-OBSERVATIONS.md +228 -0
- package/docs/30-TASK-CONTEXT-CONSUMPTION-CLOSURE-DESIGN.md +563 -0
- package/docs/31-TASK-CONTEXT-INTEGRITY-REPAIR-DESIGN.md +281 -0
- package/docs/32-PROJECT-TO-TASK-INTERACTION-PROPOSAL.md +173 -0
- package/docs/33-BOUNDED-TASK-HANDOFF-REDESIGN.md +207 -0
- package/docs/34-A0-CODEX-HOST-FEASIBILITY.md +61 -0
- package/docs/35-CONTEXT-FIRST-TASK-HANDOFF-DESIGN.md +248 -0
- package/docs/36-PRODUCT-VALUE-AND-USABILITY-REVIEW.md +158 -0
- package/docs/37-EVERYDAY-HANDOFF-FLOW-DESIGN.md +271 -0
- package/docs/38-EVERYDAY-HANDOFF-OPERATION.md +147 -0
- package/docs/39-SELF-HOST-RELEASE-ACCEPTANCE.md +39 -0
- package/docs/AI-PROJECT-INITIALIZATION.md +8 -3
- package/docs/PRODUCT-SHARING-AND-ADOPTION-GUIDE.md +111 -0
- package/docs/README.md +26 -2
- package/docs/USER-AND-AI-OPERATION-MANUAL.md +9 -3
- package/docs/assets/product-sharing-01-overview.svg +32 -0
- package/docs/assets/product-sharing-02-how-it-works.svg +17 -0
- package/docs/assets/product-sharing-03-example.svg +19 -0
- package/examples/package.json +1 -1
- package/migration-manifest.json +38 -12
- package/package.json +2 -2
- package/schemas/capabilities.schema.json +26 -12
- package/schemas/context-bundle.schema.json +32 -0
- package/schemas/coverage-audit.schema.json +6 -4
- package/schemas/evidence-bundle.schema.json +1 -1
- package/schemas/initialization-instruction.schema.json +17 -5
- package/schemas/migration-manifest.schema.json +3 -3
- package/schemas/migration-plan.schema.json +1 -1
- package/schemas/project-brief.schema.json +400 -0
- package/schemas/projection-lock.schema.json +1 -1
- package/schemas/task-context-record.schema.json +27 -0
- package/schemas/task-handoff-input.schema.json +23 -0
- package/schemas/task-handoff-result.schema.json +320 -0
- package/schemas/upgrade-assessment.schema.json +1 -1
- package/schemas/upgrade-result-bundle.schema.json +1 -1
- package/schemas/work-view.schema.json +541 -0
- package/src/project-context/adaptive-context-schema.mjs +1 -1
- package/src/project-context/adaptive-context.mjs +16 -29
- package/src/project-context/ai-entry.mjs +28 -12
- package/src/project-context/approver.mjs +6 -2
- package/src/project-context/authoring.mjs +28 -8
- package/src/project-context/capabilities.mjs +13 -1
- package/src/project-context/checker.mjs +1 -1
- package/src/project-context/cli.mjs +74 -17
- package/src/project-context/context-bundle.mjs +379 -0
- package/src/project-context/contract-schema.mjs +62 -9
- package/src/project-context/coverage-profile.mjs +127 -0
- package/src/project-context/exchange-schema.mjs +24 -6
- package/src/project-context/exchange.mjs +3 -0
- package/src/project-context/initialization-instruction.mjs +20 -2
- package/src/project-context/maintenance.mjs +5 -4
- package/src/project-context/migration-manifest.mjs +3 -3
- package/src/project-context/path-policy.mjs +13 -0
- package/src/project-context/renderer.mjs +11 -4
- package/src/project-context/source-reader.mjs +27 -2
- package/src/project-context/task-context.mjs +4 -14
- package/src/project-context/task-handoff-schema.mjs +185 -0
- package/src/project-context/task-handoff.mjs +259 -0
- package/src/project-context/upgrade-schema.mjs +1 -1
- package/src/project-context/upgrade.mjs +1 -0
- package/src/project-context/work-view-schema.mjs +125 -0
- package/src/project-context/work-view.mjs +135 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { canonicalJson, prettyCanonicalJson } from "./canonical-json.mjs";
|
|
2
|
+
import { fail } from "./errors.mjs";
|
|
3
|
+
import { validateWorkView } from "./work-view-schema.mjs";
|
|
4
|
+
|
|
5
|
+
export const WORK_VIEW_MAX_BYTES = 1024 * 1024;
|
|
6
|
+
const priority = { policy: 0, "validation-description": 1, fact: 2, reference: 3 };
|
|
7
|
+
const intentNames = { investigate: "调查", design: "设计", implement: "实施" };
|
|
8
|
+
const ranked = (items) => [...items].sort((a, b) => priority[a.kind] - priority[b.kind] || a.id.localeCompare(b.id));
|
|
9
|
+
const excerpt = (value, limit = 240) => {
|
|
10
|
+
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
11
|
+
return text.length <= limit ? text : `${text.slice(0, limit)}…(预览省略,须消费完整内容)`;
|
|
12
|
+
};
|
|
13
|
+
const progress = (record) => record ? { revision: record.revision, identity: "host-reported", ...structuredClone(record.progress) } : null;
|
|
14
|
+
|
|
15
|
+
function constraints(items) {
|
|
16
|
+
return ranked(items).slice(0, 5).map((x) => ({ text: excerpt(`${x.statement};内容:${JSON.stringify(x.value)}`), itemIds: [x.id], scope: x.scope }));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function issue(gap, record) {
|
|
20
|
+
const object = gap.path ?? gap.evidenceId ?? gap.requirementId ?? gap.decisionId ?? gap.id;
|
|
21
|
+
const descriptions = {
|
|
22
|
+
"required-not-read": ["Host", `还需读取 ${object} 并核对当前内容。`, "读取该文件后提交本次消费声明。"],
|
|
23
|
+
"context-not-consumed": ["Host", "当前 Context 尚未提交本次消费声明。", "实际阅读 required、判断 conditional,并核对任务证据后复查。"],
|
|
24
|
+
"requirements-not-consumed": ["Host", "声明没有绑定当前需求与依据。", "核对最新需求摘要并重新消费;不要求用户重述未变需求。"],
|
|
25
|
+
"consumption-task-mismatch": ["Host", "消费声明不属于当前任务。", "使用当前任务身份重新消费。"],
|
|
26
|
+
"evidence-missing": ["Host", `任务依据 ${object} 缺失。`, "在授权范围内定位具体依据;无法找到时报告缺口。"],
|
|
27
|
+
"evidence-changed": ["Host", `任务依据 ${object} 已变化。`, "读取并比较最新内容;必要的业务决定变化才请人确认。"],
|
|
28
|
+
"decision-open": ["用户", record?.decisions.find((x) => x.id === gap.decisionId)?.question ?? `决定 ${object} 尚未确认。`, "回答该具体问题;Host 不推断确认值。"],
|
|
29
|
+
"read-target-needs-file-scope": ["Host", `${object} 是目录,不能作为已读文件。`, "在 targetRoot 内收窄到实际文件并重新交接。"],
|
|
30
|
+
"target-path-not-yet-known": ["Host", "目标路径尚未锁定。", "消费项目范围 Context,在 targetRoot 内有限定位,再提交完整最小文件集合。"],
|
|
31
|
+
"project-needs-governance": ["维护者", "项目来源、批准或入口需要维护,当前交接保持阻断。", "运行 sync,审查并处理精确维护项,恢复 clean 后继续。"],
|
|
32
|
+
"work-view-budget-exceeded": ["Host", "工作交付超过上限,完整内容未交付。", "收窄具体目标或证据;概览枚举过大时提供明确 taskId。无新事实不重复重试。"],
|
|
33
|
+
};
|
|
34
|
+
const [actor, explanation, nextStep] = descriptions[gap.code] ?? ["Host", `${gap.code}:${object}`, "核对该对象和原始缺口;不能忽略后开工。"];
|
|
35
|
+
return { code: gap.code, object, explanation, actor, nextStep };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function presentation(base, context, record) {
|
|
39
|
+
const items = context?.items ?? [];
|
|
40
|
+
const issues = (base.gaps ?? base.findingCodes?.map((code) => ({ id: code, code })) ?? []).map((x) => issue(x, record));
|
|
41
|
+
const active = base.tasks?.filter((x) => x.lifecycle === "active") ?? [];
|
|
42
|
+
const questions = base.questions?.filter((x) => x.blockingNow).map((x) => ({ decisionId: x.id, question: x.question })) ?? [];
|
|
43
|
+
const basis = record ? [
|
|
44
|
+
...record.requirements.map((x) => ({ text: excerpt(x.text), requirementIds: [x.id], evidenceIds: x.evidenceIds })),
|
|
45
|
+
...record.decisions.filter((x) => x.status === "confirmed").map((x) => ({ text: excerpt(`${x.question}:${x.value}`), decisionIds: [x.id], evidenceIds: x.evidenceIds })),
|
|
46
|
+
] : [];
|
|
47
|
+
let nextStep;
|
|
48
|
+
if (base.kind === "project-brief") {
|
|
49
|
+
nextStep = base.connection === "not-connected" ? "Host 先读取包内 instructions,按唯一初始化指引接入。" : base.connection !== "available" ? "维护者按项目 findings 处理治理或入口问题。" : active.length ? "直接点名任务标题继续,或描述新需求;有歧义时只核对一次选择。" : "直接描述要改的现象或功能,给出现有设计,或请求只读盘点。";
|
|
50
|
+
} else {
|
|
51
|
+
nextStep = base.gate.status === "not-applicable" ? "该任务已结束;可查看历史,不自动恢复实施。" : base.gate.status === "hold" ? (issues[0]?.nextStep ?? base.gate.instruction) : `Host 核对当前用户授权后继续${intentNames[base.intent]};门禁不授予权限。`;
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
headline: record ? `任务「${excerpt(base.taskIdentity.title)}」:当前${intentNames[base.intent]};${base.gate.status === "hold" ? "需处理缺口" : base.gate.status === "not-applicable" ? "已结束" : "当前上下文就绪"}。` : `项目「${excerpt(base.project?.name ?? "未接入")}」:${base.connection === "available" ? "规则可用,目前没有选定开发任务" : "需要接入或维护"}。`,
|
|
55
|
+
constraints: constraints(items), remainingConstraints: Math.max(0, items.length - 5),
|
|
56
|
+
basis: basis.slice(0, 3), remainingBasis: Math.max(0, basis.length - 3),
|
|
57
|
+
progress: progress(record), issues, questions, nextStep,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function measure(value) {
|
|
62
|
+
for (let i = 0; i < 8; i += 1) {
|
|
63
|
+
const bytes = Buffer.byteLength(prettyCanonicalJson(value));
|
|
64
|
+
if (bytes === value.delivery.bytes) return bytes;
|
|
65
|
+
value.delivery.bytes = bytes;
|
|
66
|
+
}
|
|
67
|
+
fail("work-view-budget-exceeded", "cannot stabilize output byte count", { exitCode: 1 });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function finalizeWorkView(value) {
|
|
71
|
+
value.delivery.bytes = 0;
|
|
72
|
+
if (measure(value) > WORK_VIEW_MAX_BYTES) {
|
|
73
|
+
const gap = { id: "delivery:budget", code: "work-view-budget-exceeded" };
|
|
74
|
+
value.context = null;
|
|
75
|
+
value.delivery = { status: "withheld", reasonCodes: [gap.code], bytes: 0, maxBytes: WORK_VIEW_MAX_BYTES };
|
|
76
|
+
if (value.kind === "task-handoff-result") {
|
|
77
|
+
value.taskSnapshot = null;
|
|
78
|
+
value.gaps = [gap]; value.questions = []; value.itemIds = []; value.scopeCoverage = [];
|
|
79
|
+
value.requiredTargets = []; value.conditionalTargets = []; value.evidenceResults = [];
|
|
80
|
+
if (value.taskStatus !== "inactive" && value.taskStatus !== "blocked") value.taskStatus = "needs-evidence";
|
|
81
|
+
value.gate = { ...value.gate, status: value.taskStatus === "inactive" ? "not-applicable" : "hold", reasonIds: [gap.id], instruction: "完整内容未交付,收窄目标后重新交接。" };
|
|
82
|
+
value.nextAction = "narrow-work-view-targets";
|
|
83
|
+
} else {
|
|
84
|
+
value.tasks = []; value.overview = { items: [], scopeSummary: [], limitations: [gap.code] };
|
|
85
|
+
value.findingCodes = [...new Set([...value.findingCodes, gap.code])]; value.nextAction = "provide-exact-task-id-or-narrow-context";
|
|
86
|
+
}
|
|
87
|
+
value.presentation = { headline: "完整工作内容未交付,当前不能据此开始任务。", constraints: [], remainingConstraints: 0, basis: [], remainingBasis: 0, progress: null, issues: [issue(gap)], questions: [], nextStep: issue(gap).nextStep };
|
|
88
|
+
if (measure(value) > WORK_VIEW_MAX_BYTES) fail("work-view-budget-exceeded", "metadata also exceeds output budget; provide a smaller explicit task record", { exitCode: 1 });
|
|
89
|
+
}
|
|
90
|
+
return validateWorkView(value);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function taskWorkView(base, context, record) {
|
|
94
|
+
const evidenceMap = new Map(base.evidenceResults.map((x) => [x.id, x.status]));
|
|
95
|
+
const taskSnapshot = record ? {
|
|
96
|
+
origin: structuredClone(record.origin), scope: structuredClone(record.scope),
|
|
97
|
+
requirements: structuredClone(record.requirements), decisions: structuredClone(record.decisions),
|
|
98
|
+
evidence: record.evidence.map((x) => ({ ...structuredClone(x), status: evidenceMap.get(x.id) ?? "not-checked" })),
|
|
99
|
+
progress: progress(record),
|
|
100
|
+
} : null;
|
|
101
|
+
return finalizeWorkView({ ...base, schemaVersion: 2, context, taskSnapshot, presentation: presentation(base, context, record), delivery: { status: context ? "ready" : "withheld", reasonCodes: context ? [] : base.gaps.map((x) => x.code), bytes: 0, maxBytes: WORK_VIEW_MAX_BYTES } });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function projectWorkView(base, context, items) {
|
|
105
|
+
const scopeMap = new Map();
|
|
106
|
+
for (const x of items) {
|
|
107
|
+
const key = canonicalJson(x.scope);
|
|
108
|
+
if (!scopeMap.has(key)) scopeMap.set(key, { scope: structuredClone(x.scope), items: 0 });
|
|
109
|
+
scopeMap.get(key).items += 1;
|
|
110
|
+
}
|
|
111
|
+
const cards = ranked(items).slice(0, 5).map((x) => ({ id: x.id, kind: x.kind, subject: x.subject, statement: x.statement, value: structuredClone(x.value), scope: structuredClone(x.scope), sourceIds: [...x.sources].sort() }));
|
|
112
|
+
const limitations = context ? [...new Set(context.gaps.map((x) => x.code).filter((x) => x !== "target-path-not-yet-known"))] : [...base.findingCodes];
|
|
113
|
+
return finalizeWorkView({ ...base, schemaVersion: 2, context, overview: { items: cards, scopeSummary: [...scopeMap.values()].sort((a, b) => canonicalJson(a.scope).localeCompare(canonicalJson(b.scope))), limitations }, presentation: presentation(base, context), delivery: { status: context ? "ready" : "withheld", reasonCodes: context ? [] : base.findingCodes, bytes: 0, maxBytes: WORK_VIEW_MAX_BYTES } });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function renderWorkView(value) {
|
|
117
|
+
const p = value.presentation;
|
|
118
|
+
const lines = [p.headline];
|
|
119
|
+
if (value.kind !== "project-brief" && p.constraints.length) lines.push("本次可追溯规则:", ...p.constraints.map((x) => `- ${x.text}`));
|
|
120
|
+
if (value.kind !== "project-brief" && p.remainingConstraints) lines.push(`还有 ${p.remainingConstraints} 条,请消费完整 Context。`);
|
|
121
|
+
if (p.basis.length) lines.push("当前依据:", ...p.basis.map((x) => `- ${x.text}`));
|
|
122
|
+
if (p.remainingBasis) lines.push(`还有 ${p.remainingBasis} 条依据,请查看完整 taskSnapshot。`);
|
|
123
|
+
if (p.progress) lines.push("进度(Host 记录):", ...p.progress.completed.map((x) => `- 已完成:${excerpt(x)}`), ...p.progress.remaining.map((x) => `- 剩余:${excerpt(x)}`));
|
|
124
|
+
if (value.kind === "project-brief") {
|
|
125
|
+
if (value.overview.items.length) lines.push("已批准知识预览(按声明范围适用):", ...value.overview.items.map((x) => `- [${x.scope.kind}${x.scope.path ? `:${x.scope.path}` : ""}] ${excerpt(`${x.statement};内容:${JSON.stringify(x.value)}`)}`));
|
|
126
|
+
if (value.overview.scopeSummary.length) lines.push("已声明范围:", ...value.overview.scopeSummary.map((x) => `- ${x.scope.kind}${x.scope.path ? `:${x.scope.path}` : ""},${x.items} 条;仅说明注册范围。`));
|
|
127
|
+
const active = value.tasks.filter((x) => x.lifecycle === "active");
|
|
128
|
+
if (active.length) lines.push("活动任务:", ...active.map((x) => `- ${excerpt(x.title)} (${x.taskId});下一步:${excerpt(x.progressSummary.nextAction)}`));
|
|
129
|
+
if (value.overview.limitations.length) lines.push(`覆盖提示:${value.overview.limitations.join("、")};未声明业务语义完整。`);
|
|
130
|
+
}
|
|
131
|
+
if (p.issues.length) lines.push("当前缺口:", ...p.issues.map((x) => `- ${x.actor}:${excerpt(x.explanation)}`));
|
|
132
|
+
if (p.questions.length) lines.push("需要决定:", ...p.questions.map((x) => `- ${excerpt(x.question)}`));
|
|
133
|
+
lines.push(`下一步:${p.nextStep}`);
|
|
134
|
+
return `${lines.join("\n")}\n`;
|
|
135
|
+
}
|