@latysh-development/monolith 0.1.0-alpha.2 → 0.1.0-alpha.4
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/README.md +24 -1
- package/dist/brief/cli.d.ts.map +1 -1
- package/dist/brief/cli.js +8 -2
- package/dist/brief/collect.d.ts.map +1 -1
- package/dist/brief/collect.js +17 -5
- package/dist/brief/freshness.d.ts +50 -7
- package/dist/brief/freshness.d.ts.map +1 -1
- package/dist/brief/freshness.js +89 -17
- package/dist/brief/git-signals.d.ts +32 -5
- package/dist/brief/git-signals.d.ts.map +1 -1
- package/dist/brief/git-signals.js +169 -39
- package/dist/brief/knowledge.d.ts +62 -0
- package/dist/brief/knowledge.d.ts.map +1 -0
- package/dist/brief/knowledge.js +123 -0
- package/dist/brief/markdown-context.d.ts +22 -0
- package/dist/brief/markdown-context.d.ts.map +1 -0
- package/dist/brief/markdown-context.js +66 -0
- package/dist/brief/reconcile.d.ts +7 -1
- package/dist/brief/reconcile.d.ts.map +1 -1
- package/dist/brief/reconcile.js +42 -2
- package/dist/brief/story.d.ts +2 -10
- package/dist/brief/story.d.ts.map +1 -1
- package/dist/brief/story.js +21 -7
- package/dist/cli/index.js +1 -1
- package/dist/cli/style.d.ts +26 -0
- package/dist/cli/style.d.ts.map +1 -0
- package/dist/cli/style.js +76 -0
- package/dist/cli/ui.d.ts.map +1 -1
- package/dist/cli/ui.js +33 -6
- package/dist/coordinatorium/cli.d.ts.map +1 -1
- package/dist/coordinatorium/cli.js +13 -0
- package/dist/finalize/cli.d.ts.map +1 -1
- package/dist/finalize/cli.js +30 -13
- package/dist/finalize/consistency.d.ts +51 -0
- package/dist/finalize/consistency.d.ts.map +1 -0
- package/dist/finalize/consistency.js +57 -0
- package/dist/finalize/core.d.ts +28 -0
- package/dist/finalize/core.d.ts.map +1 -1
- package/dist/finalize/core.js +186 -41
- package/dist/ideas/cli.d.ts +1 -0
- package/dist/ideas/cli.d.ts.map +1 -1
- package/dist/ideas/cli.js +14 -3
- package/dist/ideas/format.d.ts +31 -1
- package/dist/ideas/format.d.ts.map +1 -1
- package/dist/ideas/format.js +109 -1
- package/dist/ideas/store.d.ts +2 -0
- package/dist/ideas/store.d.ts.map +1 -1
- package/dist/ideas/store.js +4 -1
- package/dist/ideas/types.d.ts +10 -0
- package/dist/ideas/types.d.ts.map +1 -1
- package/dist/ideas/types.js +3 -0
- package/dist/readmodel/dashboard.d.ts.map +1 -1
- package/dist/readmodel/dashboard.js +19 -1
- package/dist/readmodel/index.d.ts +2 -1
- package/dist/readmodel/index.d.ts.map +1 -1
- package/dist/readmodel/index.js +1 -0
- package/dist/readmodel/roadmap.d.ts +34 -0
- package/dist/readmodel/roadmap.d.ts.map +1 -0
- package/dist/readmodel/roadmap.js +254 -0
- package/dist/readmodel/types.d.ts +88 -0
- package/dist/readmodel/types.d.ts.map +1 -1
- package/dist/root/safe-write.d.ts +15 -4
- package/dist/root/safe-write.d.ts.map +1 -1
- package/dist/root/safe-write.js +264 -50
- package/dist/ui/apple-touch-icon.png +0 -0
- package/dist/ui/assets/index-BJKxUDPD.css +1 -0
- package/dist/ui/assets/index-ioC9DPHQ.js +28 -0
- package/dist/ui/assets/monolith-mark-Ct0ammrU.webp +0 -0
- package/dist/ui/favicon-16x16.png +0 -0
- package/dist/ui/favicon-32x32.png +0 -0
- package/dist/ui/favicon.ico +0 -0
- package/dist/ui/index.html +6 -2
- package/dist/update/cli.d.ts +1 -1
- package/dist/update/cli.d.ts.map +1 -1
- package/dist/update/cli.js +10 -3
- package/dist/update/index.d.ts +9 -0
- package/dist/update/index.d.ts.map +1 -1
- package/dist/update/index.js +50 -1
- package/dist/update/legacy-skills.d.ts +15 -0
- package/dist/update/legacy-skills.d.ts.map +1 -0
- package/dist/update/legacy-skills.js +20 -0
- package/dist/validate/index.d.ts.map +1 -1
- package/dist/validate/index.js +4 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -2
- package/templates/default-instance/monolith/context/MONOLITH-CHAT-CONTEXT.md +183 -0
- package/templates/default-instance/monolith/skills/finalize-story/SKILL.md +19 -3
- package/dist/ui/assets/index-C4NhMaq7.js +0 -28
- package/dist/ui/assets/index-DMZ7ZvZY.css +0 -1
package/README.md
CHANGED
|
@@ -101,6 +101,29 @@ monolith ui # Mission Control at http://127.0.0.1:4173 ; Ctrl+C to stop
|
|
|
101
101
|
instance, and it does not open a browser — open the printed address yourself.
|
|
102
102
|
All commands: [CLI reference](./docs/reference/cli.md).
|
|
103
103
|
|
|
104
|
+
### 🧠 One More Thing: Give Your AI Chat the Context!
|
|
105
|
+
|
|
106
|
+
MONOLITH is installed. Now make sure your AI assistant knows how to use it.
|
|
107
|
+
|
|
108
|
+
Open `.monolith/context/MONOLITH-CHAT-CONTEXT.md` and copy its contents into your ChatGPT Project instructions, Claude conversation, or another AI workspace you use to coordinate development.
|
|
109
|
+
|
|
110
|
+
It explains MONOLITH's capabilities, project memory, workflows, and limitations.
|
|
111
|
+
|
|
112
|
+
**Your coding agent gets the protocols. Your working chat gets the context.**
|
|
113
|
+
|
|
114
|
+
`monolith init` creates this file. Print it from the project root (the directory where you ran `monolith init`):
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
cat .monolith/context/MONOLITH-CHAT-CONTEXT.md
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
If `monolithRoot` in `monolith.config.json` is not `.monolith`, use that directory instead.
|
|
121
|
+
Projects initialized with an earlier MONOLITH version do not get this file from
|
|
122
|
+
`monolith update`; copy it from the template in this repository (replace
|
|
123
|
+
`{{MONOLITH_ROOT}}` with `.monolith`, otherwise `monolith validate` reports an
|
|
124
|
+
unreplaced placeholder). You can also read the template before installing:
|
|
125
|
+
[`templates/default-instance/monolith/context/MONOLITH-CHAT-CONTEXT.md`](./templates/default-instance/monolith/context/MONOLITH-CHAT-CONTEXT.md).
|
|
126
|
+
|
|
104
127
|
## Updating an Existing Project
|
|
105
128
|
|
|
106
129
|
If a project was initialised with an earlier MONOLITH version, bring it up to the current agent integration (agent entry point and Idea Capture) with:
|
|
@@ -160,7 +183,7 @@ listing — not an official partnership, endorsement or certification.
|
|
|
160
183
|
|
|
161
184
|
## Project Status
|
|
162
185
|
|
|
163
|
-
**Alpha** (`0.1.0-alpha.
|
|
186
|
+
**Alpha** (`0.1.0-alpha.4`). The API and CLI are experimental and may change
|
|
164
187
|
without notice. Published to npm under the alpha dist-tag. Durable State and the Coordinatorium
|
|
165
188
|
(`monolith durable`, `monolith mission`) are experimental and are not part of
|
|
166
189
|
the Quick Start.
|
package/dist/brief/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/brief/cli.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/brief/cli.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;GAcG;AAOH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,IAAI,CAAC;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,iGAAiG;IACjG,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,YAAY,CAAC;AAIzD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CA6C9D;AAQD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BvF"}
|
package/dist/brief/cli.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* -> 0, whatever the Freshness; usage error -> 1). It does not combine with
|
|
15
15
|
* `--strict`, whose meaning (Completeness) belongs to the brief.
|
|
16
16
|
*/
|
|
17
|
+
import { colorEnabled, paint, tintStatuses } from '../cli/style.js';
|
|
17
18
|
import { collect } from './collect.js';
|
|
18
19
|
import { render, DEFAULT_MAX_CHARS } from './render.js';
|
|
19
20
|
import { reconcile, renderReconciliation } from './reconcile.js';
|
|
@@ -60,6 +61,11 @@ export function parseBriefArgs(args) {
|
|
|
60
61
|
}
|
|
61
62
|
return reconcileMode ? { ok: true, maxChars, strict, reconcile: true } : { ok: true, maxChars, strict };
|
|
62
63
|
}
|
|
64
|
+
/** Interactive terminals only: tints the Freshness / Completeness verdict words; plain text otherwise (byte for byte). */
|
|
65
|
+
function tintBrief(text) {
|
|
66
|
+
const on = colorEnabled();
|
|
67
|
+
return tintStatuses(on ? text.replace(/^MONOLITH [A-Z ]+$/m, (h) => paint(h, 'head', true)) : text, on, ['Freshness', 'Completeness']);
|
|
68
|
+
}
|
|
63
69
|
export async function runBrief(args, targetDir = process.cwd()) {
|
|
64
70
|
const parsed = parseBriefArgs(args);
|
|
65
71
|
if (!parsed.ok) {
|
|
@@ -70,11 +76,11 @@ export async function runBrief(args, targetDir = process.cwd()) {
|
|
|
70
76
|
if (!model.freshness || !model.freshnessEvidence) {
|
|
71
77
|
throw new Error('monolith brief --reconcile: no freshness evidence could be collected.');
|
|
72
78
|
}
|
|
73
|
-
process.stdout.write(renderReconciliation(reconcile(model.freshnessEvidence, model.freshness), parsed.maxChars).text);
|
|
79
|
+
process.stdout.write(tintBrief(renderReconciliation(reconcile(model.freshnessEvidence, model.freshness), parsed.maxChars).text));
|
|
74
80
|
process.exit(0);
|
|
75
81
|
}
|
|
76
82
|
const { text, completeness } = render(model, parsed.maxChars);
|
|
77
|
-
process.stdout.write(text);
|
|
83
|
+
process.stdout.write(tintBrief(text));
|
|
78
84
|
if (completeness === 'INCOMPLETE') {
|
|
79
85
|
console.error('monolith brief: result is INCOMPLETE — see the Diagnostics section above.');
|
|
80
86
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/brief/collect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/brief/collect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAyBH,OAAO,KAAK,EACV,UAAU,EAOX,MAAM,YAAY,CAAC;AAmBpB,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,QAAa,CAAC;AAwUnD,wBAAsB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAqEpE"}
|
package/dist/brief/collect.js
CHANGED
|
@@ -16,11 +16,13 @@ import { readBriefConfig } from './config.js';
|
|
|
16
16
|
import { resolveMonolithRootValue, checkContainedFile, readBoundedFileStable, isReadSecurityViolation, findWriteArtifacts, describeWriteArtifacts, } from '../root/index.js';
|
|
17
17
|
import { discoverDocs } from './docs.js';
|
|
18
18
|
import { extractCurrentStory } from './story.js';
|
|
19
|
+
import { maskNonContent } from './markdown-context.js';
|
|
19
20
|
import { selectActiveMissions, selectRecentFinishedMissions, selectLatestCheckpoint, hasCheckpointOrderMismatch, } from './selectors.js';
|
|
20
21
|
import { stateFilePath, validateState, createInitialState, MAX_STATE_BYTES } from '../coordinatorium/index.js';
|
|
21
22
|
import { ACTIVE_MD_REL, PARKED_IDEAS_LIMIT, PROTOCOLS_DIR, ROADMAP_INDEX_REL, STORY_INDEX_REL, HANDOFF_REL } from './constants.js';
|
|
22
|
-
import { evaluateFreshness, parseActiveStoryClaim, parseRoadmapActiveStory, parseRoadmapStatuses, parseStoryIndexStatuses, } from './freshness.js';
|
|
23
|
-
import { readGitSignals } from './git-signals.js';
|
|
23
|
+
import { evaluateFreshness, extractWorkItemIds, parseActiveStoryClaim, parseRoadmapActiveStory, parseRoadmapStatuses, parseStoryIndexStatuses, } from './freshness.js';
|
|
24
|
+
import { readBlobAt, readGitSignals } from './git-signals.js';
|
|
25
|
+
import { attributeKnowledge } from './knowledge.js';
|
|
24
26
|
import { ideaStore, listIdeas } from '../ideas/index.js';
|
|
25
27
|
import { ideaNumber } from '../ideas/format.js';
|
|
26
28
|
const RECENT_FINISHED_MISSIONS_LIMIT = 5;
|
|
@@ -202,7 +204,9 @@ function collectStory(targetDir, monolithRoot, root, diagnostics) {
|
|
|
202
204
|
diagnostics.push({
|
|
203
205
|
code: 'ACTIVE_MD_NO_CURRENT_STORY',
|
|
204
206
|
severity: 'info',
|
|
205
|
-
message:
|
|
207
|
+
message: extraction.ambiguous
|
|
208
|
+
? `${relPath} has more than one "## Current Story" section outside code blocks and comments; the section is ambiguous and was not read.`
|
|
209
|
+
: `${relPath} exists but has no "## Current Story" section.`,
|
|
206
210
|
});
|
|
207
211
|
}
|
|
208
212
|
return {
|
|
@@ -267,13 +271,21 @@ function collectFreshness(targetDir, monolithRoot, root, story) {
|
|
|
267
271
|
const content = new Map(docs.map((rel) => [rel, readStateDocument(targetDir, monolithRoot, root, rel)]));
|
|
268
272
|
const roadmap = content.get(ROADMAP_INDEX_REL) ?? null;
|
|
269
273
|
const storyIndex = content.get(STORY_INDEX_REL) ?? null;
|
|
274
|
+
const git = readGitSignals(targetDir, stateFiles);
|
|
270
275
|
const evidence = {
|
|
271
|
-
git
|
|
276
|
+
git,
|
|
277
|
+
knowledge: attributeKnowledge(git, {
|
|
278
|
+
monolithRoot,
|
|
279
|
+
stateFiles: new Set(stateFiles),
|
|
280
|
+
readBlob: (sha, rel) => readBlobAt(targetDir, sha, rel),
|
|
281
|
+
hasWorkItem: (subject) => extractWorkItemIds(subject).length > 0,
|
|
282
|
+
}),
|
|
272
283
|
activeStory: story.fileExists && story.hasCurrentStorySection ? parseActiveStoryClaim(story.excerpt) : { kind: 'unknown' },
|
|
273
284
|
roadmapActiveStory: roadmap === null ? null : parseRoadmapActiveStory(roadmap),
|
|
274
285
|
roadmapStatuses: roadmap === null ? new Map() : parseRoadmapStatuses(roadmap),
|
|
275
286
|
storyIndexStatuses: storyIndex === null ? new Map() : parseStoryIndexStatuses(storyIndex),
|
|
276
|
-
|
|
287
|
+
// Ids shown only inside code fences or HTML comments are examples, not records.
|
|
288
|
+
recordedText: [...content.values()].filter((c) => c !== null).map(maskNonContent).join('\n'),
|
|
277
289
|
stateFiles: new Set(stateFiles),
|
|
278
290
|
};
|
|
279
291
|
return { result: evaluateFreshness(evidence), evidence };
|
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
* absence of an active story alone never produce STALE, and the
|
|
20
20
|
* absence of an active story never means "project complete".
|
|
21
21
|
*/
|
|
22
|
+
import { maskNonContent } from './markdown-context.js';
|
|
23
|
+
import type { KnowledgeEvidence } from './knowledge.js';
|
|
22
24
|
export type FreshnessStatus = 'FRESH' | 'STALE' | 'UNKNOWN';
|
|
23
|
-
export type FreshnessReasonCode = 'STATE_CONTRADICTION' | 'UNRECORDED_WORK' | 'GIT_UNAVAILABLE' | 'STATE_NOT_IN_GIT' | 'GIT_HISTORY_TRUNCATED' | 'UNATTRIBUTED_ACTIVITY' | 'NO_ACTIVITY_SINCE_STATE_UPDATE' | 'ACTIVITY_RECORDED';
|
|
25
|
+
export type FreshnessReasonCode = 'STATE_CONTRADICTION' | 'UNRECORDED_WORK' | 'GIT_UNAVAILABLE' | 'STATE_NOT_IN_GIT' | 'GIT_HISTORY_TRUNCATED' | 'GIT_HISTORY_SHALLOW' | 'UNATTRIBUTED_ACTIVITY' | 'NO_ACTIVITY_SINCE_STATE_UPDATE' | 'ACTIVITY_RECORDED';
|
|
24
26
|
export interface FreshnessReason {
|
|
25
27
|
code: FreshnessReasonCode;
|
|
26
28
|
message: string;
|
|
@@ -36,7 +38,7 @@ export interface GitCommitSignal {
|
|
|
36
38
|
/** Paths changed by the commit, relative to the project directory. Empty for plain merges. */
|
|
37
39
|
files: string[];
|
|
38
40
|
}
|
|
39
|
-
export type GitUnavailableReason = 'GIT_NOT_INSTALLED' | 'NOT_A_GIT_REPOSITORY' | 'GIT_FAILED';
|
|
41
|
+
export type GitUnavailableReason = 'GIT_NOT_INSTALLED' | 'NOT_A_GIT_REPOSITORY' | 'GIT_FAILED' | 'GIT_OUTPUT_INVALID';
|
|
40
42
|
export type GitSignals = {
|
|
41
43
|
status: 'unavailable';
|
|
42
44
|
reason: GitUnavailableReason;
|
|
@@ -51,14 +53,22 @@ export type GitSignals = {
|
|
|
51
53
|
commitsSince: GitCommitSignal[];
|
|
52
54
|
/** True when git history was cut at the read limit — the picture is partial. */
|
|
53
55
|
truncated: boolean;
|
|
56
|
+
/** True in a shallow clone: history before the boundary is absent, so "no later activity" proves nothing. */
|
|
57
|
+
shallow?: boolean;
|
|
54
58
|
};
|
|
55
59
|
export type StoryClaim = {
|
|
56
60
|
kind: 'none';
|
|
57
61
|
} | {
|
|
58
62
|
kind: 'ids';
|
|
59
63
|
ids: string[];
|
|
60
|
-
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* `contradiction`: the section both says "no active story" and has a
|
|
67
|
+
* structural `**Story:**` field (PSC-004A) — the field value(s), verbatim.
|
|
68
|
+
*/
|
|
69
|
+
| {
|
|
61
70
|
kind: 'unknown';
|
|
71
|
+
contradiction?: string[];
|
|
62
72
|
};
|
|
63
73
|
export interface FreshnessEvidence {
|
|
64
74
|
git: GitSignals;
|
|
@@ -73,6 +83,8 @@ export interface FreshnessEvidence {
|
|
|
73
83
|
recordedText: string;
|
|
74
84
|
/** Repo-relative-to-project paths that are state documents (their own commits are not "activity"). */
|
|
75
85
|
stateFiles: ReadonlySet<string>;
|
|
86
|
+
/** KA-001: commits explained by registered independent knowledge. Absent = none (older callers). */
|
|
87
|
+
knowledge?: KnowledgeEvidence;
|
|
76
88
|
}
|
|
77
89
|
export interface FreshnessResult {
|
|
78
90
|
status: FreshnessStatus;
|
|
@@ -92,13 +104,44 @@ export interface FreshnessResult {
|
|
|
92
104
|
}
|
|
93
105
|
export declare function extractWorkItemIds(text: string): string[];
|
|
94
106
|
export declare function mentionsId(text: string, id: string): boolean;
|
|
95
|
-
|
|
107
|
+
export { maskNonContent };
|
|
108
|
+
/**
|
|
109
|
+
* Interprets the `## Current Story` excerpt of active.md.
|
|
110
|
+
*
|
|
111
|
+
* Identity comes from the structural `**Story:** <id> …` field only (first
|
|
112
|
+
* token = id), exactly as DOCSYNC reads it. Ids in titles, prose, code fences
|
|
113
|
+
* or comments are never identity. Zero usable, several or malformed fields →
|
|
114
|
+
* `unknown` (never a guess, never STALE on its own).
|
|
115
|
+
*
|
|
116
|
+
* Compatibility: a historical section with no `**Story:**` field at all is
|
|
117
|
+
* still read when its first line is a *bold declaration* of exactly one
|
|
118
|
+
* work-item id — `**WIDGET-001 — title**`, `**WIDGET-001**` or
|
|
119
|
+
* `**WIDGET-001** — status` (the form old active.md files used). An unbolded
|
|
120
|
+
* line (`STORY-001 was completed yesterday.`, `STORY-001 — done`) is
|
|
121
|
+
* indistinguishable from a sentence that merely mentions a story, so it is
|
|
122
|
+
* `unknown`, never a confirmed identity.
|
|
123
|
+
*
|
|
124
|
+
* "No active story" as the first visible line is `none` only when the section
|
|
125
|
+
* has no structural `**Story:**` field at all; with one (valid, duplicate or
|
|
126
|
+
* malformed) the section contradicts itself → `unknown` with `contradiction`
|
|
127
|
+
* (PSC-004A, same rule as DOCSYNC's `activeStoryIdentity()`).
|
|
128
|
+
*/
|
|
96
129
|
export declare function parseActiveStoryClaim(excerpt: string | null): StoryClaim;
|
|
97
|
-
/** `Active Story: <text>` line of roadmap-index.md. */
|
|
130
|
+
/** `Active Story: <text>` line of roadmap-index.md (outside code fences and comments). */
|
|
98
131
|
export declare function parseRoadmapActiveStory(content: string): string | null;
|
|
99
|
-
/** Table rows `| id | title | STATUS | date |` of roadmap-index.md. */
|
|
132
|
+
/** Table rows `| id | title | STATUS | date |` of roadmap-index.md (outside code fences and comments). */
|
|
100
133
|
export declare function parseRoadmapStatuses(content: string): Map<string, string>;
|
|
101
|
-
/** `### [STORY ]<id> — title` followed by `**Status:** X` in story-index.md. */
|
|
134
|
+
/** `### [STORY ]<id> — title` followed by `**Status:** X` in story-index.md (outside code fences and comments). */
|
|
102
135
|
export declare function parseStoryIndexStatuses(content: string): Map<string, string>;
|
|
136
|
+
export interface Contradiction {
|
|
137
|
+
message: string;
|
|
138
|
+
documents: string[];
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Contradictions between the state documents. Shared by `brief` (freshness /
|
|
142
|
+
* `--reconcile`) and `finalize` (FIN-002 consistency assessment) so that there
|
|
143
|
+
* is exactly one definition of "these documents disagree".
|
|
144
|
+
*/
|
|
145
|
+
export declare function findContradictions(ev: Pick<FreshnessEvidence, 'activeStory' | 'roadmapActiveStory' | 'roadmapStatuses' | 'storyIndexStatuses'>): Contradiction[];
|
|
103
146
|
export declare function evaluateFreshness(ev: FreshnessEvidence): FreshnessResult;
|
|
104
147
|
//# sourceMappingURL=freshness.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"freshness.d.ts","sourceRoot":"","sources":["../../src/brief/freshness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;
|
|
1
|
+
{"version":3,"file":"freshness.d.ts","sourceRoot":"","sources":["../../src/brief/freshness.ts"],"names":[],"mappings":"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"}
|
package/dist/brief/freshness.js
CHANGED
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
* absence of an active story alone never produce STALE, and the
|
|
20
20
|
* absence of an active story never means "project complete".
|
|
21
21
|
*/
|
|
22
|
+
import { isIdeaId } from '../ideas/format.js';
|
|
23
|
+
import { declaresNoActiveStory, maskNonContent } from './markdown-context.js';
|
|
22
24
|
// ---------------------------------------------------------------------------
|
|
23
25
|
// Work-item ids
|
|
24
26
|
// ---------------------------------------------------------------------------
|
|
@@ -30,8 +32,14 @@
|
|
|
30
32
|
const WORK_ITEM_SOURCE = String.raw `(?:[A-Z]{2,}-)+\d{3}[A-Z]?(?:\.\d+)*(?:-[A-Z])?`;
|
|
31
33
|
/** Decision / lesson records are not work items. */
|
|
32
34
|
const NON_WORK_PREFIXES = ['ADR-', 'DEC-', 'LESSON-'];
|
|
35
|
+
/**
|
|
36
|
+
* Ideas live in their own namespace (`IDEA-NNN`, src/ideas/format.ts) and are
|
|
37
|
+
* stored in the idea store, not in the state documents. Only the exact idea
|
|
38
|
+
* grammar is excluded: `IDEA-001A`, `IDEA-001B.1` or `IDEA-003-R` are story
|
|
39
|
+
* ids that merely share the prefix and stay work items.
|
|
40
|
+
*/
|
|
33
41
|
function isWorkItem(id) {
|
|
34
|
-
return !NON_WORK_PREFIXES.some((p) => id.startsWith(p));
|
|
42
|
+
return !NON_WORK_PREFIXES.some((p) => id.startsWith(p)) && !isIdeaId(id);
|
|
35
43
|
}
|
|
36
44
|
export function extractWorkItemIds(text) {
|
|
37
45
|
const re = new RegExp(`(?<![A-Za-z0-9-])${WORK_ITEM_SOURCE}(?![A-Za-z0-9]|\\.\\d|-[A-Z0-9])`, 'g');
|
|
@@ -46,29 +54,69 @@ export function mentionsId(text, id) {
|
|
|
46
54
|
const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
47
55
|
return new RegExp(`(?<![A-Za-z0-9-])${escaped}(?![A-Za-z0-9]|\\.\\d|-[A-Z0-9])`).test(text);
|
|
48
56
|
}
|
|
57
|
+
// `maskNonContent` lives in ./markdown-context.ts (shared with story extraction
|
|
58
|
+
// and DOCSYNC) and is re-exported here for existing importers.
|
|
59
|
+
export { maskNonContent };
|
|
49
60
|
// ---------------------------------------------------------------------------
|
|
50
61
|
// Parsers for the state documents (confirmed structure only)
|
|
51
62
|
// ---------------------------------------------------------------------------
|
|
52
63
|
const NONE_RE = /^\W*(?:no active story|none|unknown)\b|^\s*[—-]\s*$/i;
|
|
53
|
-
/**
|
|
64
|
+
/** Same id grammar as DOCSYNC's `activeStoryIdentity()`. */
|
|
65
|
+
const STORY_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
66
|
+
/**
|
|
67
|
+
* Interprets the `## Current Story` excerpt of active.md.
|
|
68
|
+
*
|
|
69
|
+
* Identity comes from the structural `**Story:** <id> …` field only (first
|
|
70
|
+
* token = id), exactly as DOCSYNC reads it. Ids in titles, prose, code fences
|
|
71
|
+
* or comments are never identity. Zero usable, several or malformed fields →
|
|
72
|
+
* `unknown` (never a guess, never STALE on its own).
|
|
73
|
+
*
|
|
74
|
+
* Compatibility: a historical section with no `**Story:**` field at all is
|
|
75
|
+
* still read when its first line is a *bold declaration* of exactly one
|
|
76
|
+
* work-item id — `**WIDGET-001 — title**`, `**WIDGET-001**` or
|
|
77
|
+
* `**WIDGET-001** — status` (the form old active.md files used). An unbolded
|
|
78
|
+
* line (`STORY-001 was completed yesterday.`, `STORY-001 — done`) is
|
|
79
|
+
* indistinguishable from a sentence that merely mentions a story, so it is
|
|
80
|
+
* `unknown`, never a confirmed identity.
|
|
81
|
+
*
|
|
82
|
+
* "No active story" as the first visible line is `none` only when the section
|
|
83
|
+
* has no structural `**Story:**` field at all; with one (valid, duplicate or
|
|
84
|
+
* malformed) the section contradicts itself → `unknown` with `contradiction`
|
|
85
|
+
* (PSC-004A, same rule as DOCSYNC's `activeStoryIdentity()`).
|
|
86
|
+
*/
|
|
54
87
|
export function parseActiveStoryClaim(excerpt) {
|
|
55
88
|
if (excerpt === null)
|
|
56
89
|
return { kind: 'unknown' };
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
90
|
+
const masked = maskNonContent(excerpt);
|
|
91
|
+
const firstLine = masked.split('\n').find((l) => l.trim() !== '') ?? '';
|
|
92
|
+
const fields = masked
|
|
93
|
+
.split('\n')
|
|
94
|
+
.map((l) => /^\*\*Story:\*\*[ \t]*(.*)$/.exec(l.replace(/\r$/, '')))
|
|
95
|
+
.filter((m) => m !== null)
|
|
96
|
+
.map((m) => m[1].trim());
|
|
97
|
+
if (declaresNoActiveStory(excerpt))
|
|
98
|
+
return fields.length === 0 ? { kind: 'none' } : { kind: 'unknown', contradiction: fields };
|
|
99
|
+
if (fields.length > 0) {
|
|
100
|
+
if (fields.length > 1)
|
|
101
|
+
return { kind: 'unknown' };
|
|
102
|
+
const id = /^(\S+)(?:\s|$)/.exec(fields[0])?.[1];
|
|
103
|
+
return id !== undefined && STORY_ID_RE.test(id) ? { kind: 'ids', ids: [id] } : { kind: 'unknown' };
|
|
104
|
+
}
|
|
105
|
+
const line = firstLine.trim();
|
|
106
|
+
const idEnd = String.raw `(?![A-Za-z0-9]|\.\d|-[A-Z0-9])`;
|
|
107
|
+
const declaration = new RegExp(`^\\*\\*(${WORK_ITEM_SOURCE})${idEnd}\\*\\*(?:\\s+[—–-](?:\\s.*)?)?$`).exec(line) ??
|
|
108
|
+
new RegExp(`^\\*\\*(${WORK_ITEM_SOURCE})${idEnd}\\s+[—–-]\\s.*\\*\\*$`).exec(line);
|
|
109
|
+
return declaration !== null && extractWorkItemIds(line).length === 1 ? { kind: 'ids', ids: [declaration[1]] } : { kind: 'unknown' };
|
|
62
110
|
}
|
|
63
|
-
/** `Active Story: <text>` line of roadmap-index.md. */
|
|
111
|
+
/** `Active Story: <text>` line of roadmap-index.md (outside code fences and comments). */
|
|
64
112
|
export function parseRoadmapActiveStory(content) {
|
|
65
|
-
const m = /^Active Story:\s*(.+?)\s*$/m.exec(content);
|
|
113
|
+
const m = /^Active Story:\s*(.+?)\s*$/m.exec(maskNonContent(content));
|
|
66
114
|
return m ? m[1] : null;
|
|
67
115
|
}
|
|
68
|
-
/** Table rows `| id | title | STATUS | date |` of roadmap-index.md. */
|
|
116
|
+
/** Table rows `| id | title | STATUS | date |` of roadmap-index.md (outside code fences and comments). */
|
|
69
117
|
export function parseRoadmapStatuses(content) {
|
|
70
118
|
const out = new Map();
|
|
71
|
-
for (const line of content.split('\n')) {
|
|
119
|
+
for (const line of maskNonContent(content).split('\n')) {
|
|
72
120
|
const cells = line.split('|').map((c) => c.trim());
|
|
73
121
|
// leading and trailing '|' produce empty first/last cells
|
|
74
122
|
if (cells.length < 5 || cells[0] !== '' || cells[cells.length - 1] !== '')
|
|
@@ -82,10 +130,10 @@ export function parseRoadmapStatuses(content) {
|
|
|
82
130
|
}
|
|
83
131
|
return out;
|
|
84
132
|
}
|
|
85
|
-
/** `### [STORY ]<id> — title` followed by `**Status:** X` in story-index.md. */
|
|
133
|
+
/** `### [STORY ]<id> — title` followed by `**Status:** X` in story-index.md (outside code fences and comments). */
|
|
86
134
|
export function parseStoryIndexStatuses(content) {
|
|
87
135
|
const out = new Map();
|
|
88
|
-
const lines = content.split('\n');
|
|
136
|
+
const lines = maskNonContent(content).split('\n');
|
|
89
137
|
for (let i = 0; i < lines.length; i++) {
|
|
90
138
|
const h = /^###\s+(?:STORY\s+)?([A-Za-z0-9][A-Za-z0-9.\-]*)\s+—/.exec(lines[i]);
|
|
91
139
|
if (!h)
|
|
@@ -115,8 +163,19 @@ function listIds(ids) {
|
|
|
115
163
|
function isDone(status) {
|
|
116
164
|
return status === 'COMPLETED' || status === 'DONE';
|
|
117
165
|
}
|
|
118
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Contradictions between the state documents. Shared by `brief` (freshness /
|
|
168
|
+
* `--reconcile`) and `finalize` (FIN-002 consistency assessment) so that there
|
|
169
|
+
* is exactly one definition of "these documents disagree".
|
|
170
|
+
*/
|
|
171
|
+
export function findContradictions(ev) {
|
|
119
172
|
const out = [];
|
|
173
|
+
if (ev.activeStory.kind === 'unknown' && ev.activeStory.contradiction !== undefined) {
|
|
174
|
+
out.push({
|
|
175
|
+
message: `active.md records no active story, but its Current Story section also has a **Story:** field (${ev.activeStory.contradiction.map((f) => `"${f.slice(0, 40)}"`).join(', ')}).`,
|
|
176
|
+
documents: ['active.md'],
|
|
177
|
+
});
|
|
178
|
+
}
|
|
120
179
|
if (ev.activeStory.kind === 'none' && ev.roadmapActiveStory !== null && !NONE_RE.test(ev.roadmapActiveStory)) {
|
|
121
180
|
out.push({
|
|
122
181
|
message: `active.md records no active story, but roadmap-index.md names "${ev.roadmapActiveStory}".`,
|
|
@@ -171,6 +230,7 @@ export function evaluateFreshness(ev) {
|
|
|
171
230
|
GIT_NOT_INSTALLED: 'git is not installed or not runnable',
|
|
172
231
|
NOT_A_GIT_REPOSITORY: 'the project is not inside a git repository',
|
|
173
232
|
GIT_FAILED: 'git could not be queried',
|
|
233
|
+
GIT_OUTPUT_INVALID: 'git returned history that could not be parsed unambiguously',
|
|
174
234
|
};
|
|
175
235
|
reasons.push({ code: 'GIT_UNAVAILABLE', message: `Cannot compare state with history: ${why[ev.git.reason]}.` });
|
|
176
236
|
gitVerdict = 'unknown';
|
|
@@ -187,9 +247,12 @@ export function evaluateFreshness(ev) {
|
|
|
187
247
|
base.activityCount = activity.length;
|
|
188
248
|
const referenced = new Set();
|
|
189
249
|
let unattributed = 0;
|
|
250
|
+
let knowledgeCommits = 0;
|
|
190
251
|
for (const c of activity) {
|
|
191
252
|
const ids = extractWorkItemIds(c.subject);
|
|
192
|
-
if (ids.length === 0)
|
|
253
|
+
if (ids.length === 0 && ev.knowledge?.attributed.has(c.sha))
|
|
254
|
+
knowledgeCommits++;
|
|
255
|
+
else if (ids.length === 0)
|
|
193
256
|
unattributed++;
|
|
194
257
|
for (const id of ids)
|
|
195
258
|
referenced.add(id);
|
|
@@ -211,6 +274,13 @@ export function evaluateFreshness(ev) {
|
|
|
211
274
|
});
|
|
212
275
|
gitVerdict = 'unknown';
|
|
213
276
|
}
|
|
277
|
+
else if (ev.git.shallow === true) {
|
|
278
|
+
reasons.push({
|
|
279
|
+
code: 'GIT_HISTORY_SHALLOW',
|
|
280
|
+
message: 'The repository is a shallow clone: history before its boundary is not available, so the check cannot be complete.',
|
|
281
|
+
});
|
|
282
|
+
gitVerdict = 'unknown';
|
|
283
|
+
}
|
|
214
284
|
else if (unattributed > 0) {
|
|
215
285
|
reasons.push({
|
|
216
286
|
code: 'UNATTRIBUTED_ACTIVITY',
|
|
@@ -229,11 +299,13 @@ export function evaluateFreshness(ev) {
|
|
|
229
299
|
else {
|
|
230
300
|
reasons.push({
|
|
231
301
|
code: 'ACTIVITY_RECORDED',
|
|
232
|
-
message:
|
|
302
|
+
message: knowledgeCommits === 0
|
|
303
|
+
? `All ${activity.length} commit(s) after the last state update reference work items that the state documents mention.`
|
|
304
|
+
: `All ${activity.length} commit(s) after the last state update either reference work items that the state documents mention (${activity.length - knowledgeCommits}) or are attributed to registered independent knowledge (${knowledgeCommits}).`,
|
|
233
305
|
});
|
|
234
306
|
gitVerdict = 'fresh';
|
|
235
307
|
}
|
|
236
|
-
if (activity.length > 0 && ev.activeStory.kind === 'none') {
|
|
308
|
+
if (activity.length - knowledgeCommits > 0 && ev.activeStory.kind === 'none') {
|
|
237
309
|
warnings.push('Git activity exists after the last recorded state update, but no active story is recorded.');
|
|
238
310
|
}
|
|
239
311
|
}
|
|
@@ -1,17 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Read-only git signals for the freshness check
|
|
2
|
+
* Read-only, local, offline git signals for the freshness check
|
|
3
|
+
* (SELF-AWARENESS-001, hardened in alpha.4 by A4-SEC-01/02).
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Contract:
|
|
6
|
+
* - Only `git log`, `git rev-list`, `git diff-tree`, `git rev-parse` and
|
|
7
|
+
* `git cat-file` are run; never a command that writes, never through a shell.
|
|
8
|
+
* - Offline: lazy fetch of missing objects (partial clones) is disabled
|
|
9
|
+
* (`GIT_NO_LAZY_FETCH=1`) and every transport is refused
|
|
10
|
+
* (`GIT_ALLOW_PROTOCOL=none`), so a missing object is an error here, never a
|
|
11
|
+
* download, an object-store write, or a program started from remote config.
|
|
12
|
+
* (`-c protocol.allow=never` alone was tested and does NOT stop a `file`
|
|
13
|
+
* promisor remote; it is kept only as an additional layer.)
|
|
14
|
+
* - Structural parsing: output that carries attacker-influenced text (commit
|
|
15
|
+
* subjects, file names) is never split on characters that text can contain.
|
|
16
|
+
* Records use NUL (`-z`), which neither a subject nor a path can contain;
|
|
17
|
+
* commit metadata and file lists come from separate commands; shas and dates
|
|
18
|
+
* are validated against strict patterns; the commit list is cross-checked
|
|
19
|
+
* against `git rev-list`. Anything unexpected is `GIT_OUTPUT_INVALID`.
|
|
20
|
+
* - Fail closed: every failure degrades to `{ status: 'unavailable' }`, which
|
|
21
|
+
* freshness turns into UNKNOWN. Git being absent, broken, refusing the
|
|
22
|
+
* directory or returning something unparseable never fails the brief and is
|
|
23
|
+
* never evidence of FRESH.
|
|
8
24
|
*/
|
|
9
25
|
import type { GitSignals } from './freshness.js';
|
|
10
26
|
/** Upper bound on commits read after the last state update. Hitting it marks the result truncated. */
|
|
11
27
|
export declare const MAX_COMMITS_SINCE = 500;
|
|
28
|
+
/** Largest blob `readBlobAt` will read; checked with `git cat-file -s` before the content is requested. */
|
|
29
|
+
export declare const MAX_BLOB_BYTES: number;
|
|
30
|
+
export declare function isGitSha(s: string): boolean;
|
|
12
31
|
/**
|
|
13
32
|
* @param targetDir project directory (git is run from here; paths are relative to it)
|
|
14
33
|
* @param stateFiles state documents, relative to `targetDir`. Only existing ones need be passed.
|
|
15
34
|
*/
|
|
16
35
|
export declare function readGitSignals(targetDir: string, stateFiles: string[]): GitSignals;
|
|
36
|
+
/**
|
|
37
|
+
* Contents of `relPath` (relative to `targetDir`) as of commit `sha`, or null
|
|
38
|
+
* when the file does not exist there, is larger than `MAX_BLOB_BYTES`, is not
|
|
39
|
+
* available locally (a partial clone's missing blob is NOT fetched) or cannot be
|
|
40
|
+
* read. `git cat-file` only: read-only, no textconv, no shell. Used by KA-001 to
|
|
41
|
+
* judge an idea registration as it was when the commit was made.
|
|
42
|
+
*/
|
|
43
|
+
export declare function readBlobAt(targetDir: string, sha: string, relPath: string): string | null;
|
|
17
44
|
//# sourceMappingURL=git-signals.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-signals.d.ts","sourceRoot":"","sources":["../../src/brief/git-signals.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"git-signals.d.ts","sourceRoot":"","sources":["../../src/brief/git-signals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,gBAAgB,CAAC;AAElE,sGAAsG;AACtG,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,2GAA2G;AAC3G,eAAO,MAAM,cAAc,QAAa,CAAC;AAqDzC,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAE3C;AAyDD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,UAAU,CA6DlF;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUzF"}
|