djournal 0.1.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/.agents/adapters/contract.md +48 -0
- package/.agents/adapters/shared/journal-hook.js +120 -0
- package/.agents/rules/AUTOMATION.md +84 -0
- package/.agents/rules/FEAT.md +83 -0
- package/.agents/rules/JOURNAL.md +127 -0
- package/.agents/rules/LINKS.md +96 -0
- package/.agents/rules/METADATA.md +179 -0
- package/.agents/rules/SAFETY.md +92 -0
- package/.agents/rules/STATE.md +31 -0
- package/.agents/skills/audit/SKILL.md +89 -0
- package/.agents/skills/audit/agents/openai.yaml +4 -0
- package/.agents/skills/decision/SKILL.md +74 -0
- package/.agents/skills/decision/agents/openai.yaml +4 -0
- package/.agents/skills/document/SKILL.md +68 -0
- package/.agents/skills/init-work/SKILL.md +62 -0
- package/.agents/skills/journal/SKILL.md +97 -0
- package/.agents/skills/journal-workflow/SKILL.md +45 -0
- package/.agents/skills/journal-workflow/agents/openai.yaml +4 -0
- package/.agents/skills/plan/SKILL.md +98 -0
- package/.agents/skills/recall/SKILL.md +67 -0
- package/.agents/skills/reconcile/SKILL.md +93 -0
- package/.agents/skills/reconcile/agents/openai.yaml +4 -0
- package/.agents/skills/research-codebase/SKILL.md +72 -0
- package/.agents/skills/research-web/SKILL.md +72 -0
- package/.agents/skills/resume/SKILL.md +68 -0
- package/.agents/skills/switch/SKILL.md +31 -0
- package/.claude/settings.json +41 -0
- package/.codex/hooks.json +40 -0
- package/AGENTS.md +23 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +202 -0
- package/README.md +158 -0
- package/bin/journal.js +118 -0
- package/install.sh +35 -0
- package/lib/installer/index.js +538 -0
- package/lib/installer/merge.js +125 -0
- package/package.json +55 -0
- package/spec.md +297 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Journal Harness Adapter Contract
|
|
2
|
+
|
|
3
|
+
Harness adapters improve compliance with the journal workflow. They are not a
|
|
4
|
+
second workflow engine and do not own durable state.
|
|
5
|
+
|
|
6
|
+
## Semantic events
|
|
7
|
+
|
|
8
|
+
| Event | Adapter behavior |
|
|
9
|
+
| --- | --- |
|
|
10
|
+
| `session_start` | Inject the active work name and tell the agent to follow `AGENTS.md` |
|
|
11
|
+
| `prompt_submit` | Remind the agent to classify the request; preserve explicit opt-out |
|
|
12
|
+
| `mutation_observed` | Optional ephemeral reminder only; never write state |
|
|
13
|
+
| `before_compact` | Optional reminder to preserve material state before context loss |
|
|
14
|
+
| `stop` | Validate the final journal status marker and request one closure pass |
|
|
15
|
+
|
|
16
|
+
An adapter may omit events its harness cannot support reliably. Instruction-only
|
|
17
|
+
behavior remains the compatibility baseline.
|
|
18
|
+
|
|
19
|
+
## Input
|
|
20
|
+
|
|
21
|
+
The shared checker accepts one JSON object on stdin. It uses only stable fields:
|
|
22
|
+
|
|
23
|
+
- `cwd`
|
|
24
|
+
- `hook_event_name`
|
|
25
|
+
- `prompt`
|
|
26
|
+
- `last_assistant_message`
|
|
27
|
+
- `stop_hook_active`
|
|
28
|
+
|
|
29
|
+
It does not parse conversation transcripts. Harness wrappers may normalize
|
|
30
|
+
native payloads to these names.
|
|
31
|
+
|
|
32
|
+
## Output
|
|
33
|
+
|
|
34
|
+
- Start and prompt events return `hookSpecificOutput.additionalContext`.
|
|
35
|
+
- Stop returns `{}` when closure is valid or no journal is installed.
|
|
36
|
+
- Stop returns `{ "decision": "block", "reason": "..." }` for one retry when
|
|
37
|
+
the final status marker is missing or invalid.
|
|
38
|
+
- Malformed input and absent journal roots fail open with a bounded stderr
|
|
39
|
+
diagnostic and exit code zero.
|
|
40
|
+
|
|
41
|
+
## Invariants
|
|
42
|
+
|
|
43
|
+
- Hooks are read-only.
|
|
44
|
+
- `stop_hook_active: true` always permits stopping.
|
|
45
|
+
- `closed` markers must resolve inside `.journal/work/` to an existing Markdown
|
|
46
|
+
spine entry.
|
|
47
|
+
- Adapters do not infer task meaning from file changes or unstable transcripts.
|
|
48
|
+
- Adding a harness must not change `AUTOMATION.md` semantics.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const fs = require("node:fs");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
|
|
8
|
+
const MARKER = /<!--\s*journal-status:\s*(closed|not-needed|off)(?:\s+([^>]*?))?\s*-->\s*$/i;
|
|
9
|
+
|
|
10
|
+
function readStdin() {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
let input = "";
|
|
13
|
+
process.stdin.setEncoding("utf8");
|
|
14
|
+
process.stdin.on("data", (chunk) => { input += chunk; });
|
|
15
|
+
process.stdin.on("end", () => resolve(input));
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function findRoot(start) {
|
|
20
|
+
let current = path.resolve(start || process.cwd());
|
|
21
|
+
while (true) {
|
|
22
|
+
if (fs.existsSync(path.join(current, "AGENTS.md")) &&
|
|
23
|
+
fs.existsSync(path.join(current, ".agents", "rules", "AUTOMATION.md"))) {
|
|
24
|
+
return current;
|
|
25
|
+
}
|
|
26
|
+
const parent = path.dirname(current);
|
|
27
|
+
if (parent === current) return null;
|
|
28
|
+
current = parent;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function activeWork(root) {
|
|
33
|
+
try {
|
|
34
|
+
const state = JSON.parse(fs.readFileSync(path.join(root, ".journal", "state.json"), "utf8"));
|
|
35
|
+
if (typeof state.active_work_name !== "string" || !state.active_work_name) return null;
|
|
36
|
+
const work = path.join(root, ".journal", "work", state.active_work_name, "work.md");
|
|
37
|
+
return fs.existsSync(work) ? state.active_work_name : null;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function context(event, message) {
|
|
44
|
+
return {
|
|
45
|
+
hookSpecificOutput: {
|
|
46
|
+
hookEventName: event,
|
|
47
|
+
additionalContext: message,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function validateClosedPath(root, value) {
|
|
53
|
+
if (!value) return false;
|
|
54
|
+
const relative = value.trim().replaceAll("\\", "/");
|
|
55
|
+
if (!relative.endsWith(".md") || path.isAbsolute(relative)) return false;
|
|
56
|
+
const resolved = path.resolve(root, relative);
|
|
57
|
+
const journalRoot = path.resolve(root, ".journal", "work") + path.sep;
|
|
58
|
+
if (!resolved.startsWith(journalRoot)) return false;
|
|
59
|
+
if (!fs.existsSync(resolved) || !fs.statSync(resolved).isFile()) return false;
|
|
60
|
+
return resolved.split(path.sep).join("/").includes("/journal/");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function handle(payload) {
|
|
64
|
+
const event = payload.hook_event_name;
|
|
65
|
+
const root = findRoot(payload.cwd);
|
|
66
|
+
if (!root) return {};
|
|
67
|
+
|
|
68
|
+
if (event === "SessionStart") {
|
|
69
|
+
const work = activeWork(root);
|
|
70
|
+
const suffix = work ? ` Active work: ${work}.` : " No valid active work is selected.";
|
|
71
|
+
return context(event, `Follow AGENTS.md and .agents/rules/AUTOMATION.md.${suffix}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (event === "UserPromptSubmit") {
|
|
75
|
+
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
76
|
+
if (/\bjournal\s*:\s*off\b/i.test(prompt)) {
|
|
77
|
+
return context(event, "Journal opt-out applies to this request. End with the journal-status: off marker.");
|
|
78
|
+
}
|
|
79
|
+
return context(event, "Classify this request using the ambient journal workflow. Close meaningful changed state before the final response, without adding ceremony to read-only or trivial work.");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (event !== "Stop") return {};
|
|
83
|
+
if (payload.stop_hook_active === true) return {};
|
|
84
|
+
|
|
85
|
+
const message = typeof payload.last_assistant_message === "string"
|
|
86
|
+
? payload.last_assistant_message
|
|
87
|
+
: "";
|
|
88
|
+
const match = message.match(MARKER);
|
|
89
|
+
if (!match) {
|
|
90
|
+
return {
|
|
91
|
+
decision: "block",
|
|
92
|
+
reason: "Perform the journal closure checkpoint from AGENTS.md. Journal meaningful changed state if needed, then end with one valid hidden journal-status marker.",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (match[1].toLowerCase() === "closed" && !validateClosedPath(root, match[2])) {
|
|
97
|
+
return {
|
|
98
|
+
decision: "block",
|
|
99
|
+
reason: "The journal-status closed marker must reference an existing repository-relative Markdown spine entry under .journal/work/<work>/journal/.",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function main() {
|
|
106
|
+
try {
|
|
107
|
+
const raw = await readStdin();
|
|
108
|
+
const payload = raw.trim() ? JSON.parse(raw) : {};
|
|
109
|
+
process.stdout.write(`${JSON.stringify(handle(payload))}\n`);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
process.stderr.write(`journal hook skipped: ${error.message}\n`);
|
|
112
|
+
process.stdout.write("{}\n");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (require.main === module) {
|
|
117
|
+
main();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = { handle };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Ambient Journal Automation Rules
|
|
2
|
+
|
|
3
|
+
These rules coordinate existing journal skills. They do not replace the file,
|
|
4
|
+
metadata, link, state, or safety rules those skills own.
|
|
5
|
+
|
|
6
|
+
## Task classification
|
|
7
|
+
|
|
8
|
+
Classify each request before acting:
|
|
9
|
+
|
|
10
|
+
| Class | Examples | Default behavior |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| Read-only | explain, inspect, review, answer status | Recall or resume only when history matters; do not write a closing entry |
|
|
13
|
+
| Trivial mutation | typo, tiny formatting or config correction | Resume only when context matters; validate; journal only if durable project state changed |
|
|
14
|
+
| Meaningful mutation | feature, fix, migration, behavioral change | Resume; plan when warranted; implement; validate; journal |
|
|
15
|
+
| Durable knowledge | research, decision, canonical documentation | Resume; use the matching supporting skill; journal when work state changed |
|
|
16
|
+
|
|
17
|
+
Classification requires semantic judgment. File changes alone do not establish
|
|
18
|
+
that a task was meaningful.
|
|
19
|
+
|
|
20
|
+
## Start checkpoint
|
|
21
|
+
|
|
22
|
+
- If the user says `journal: off`, skip ambient journal reads and writes for the
|
|
23
|
+
request unless another explicit instruction requires them.
|
|
24
|
+
- If `.journal/state.json` selects active work, use `resume` when historical
|
|
25
|
+
context can affect the result.
|
|
26
|
+
- If no active work exists and the request clearly starts a new durable work
|
|
27
|
+
stream, use `init-work`. Ask only when its identity or scope is materially
|
|
28
|
+
ambiguous.
|
|
29
|
+
- Do not create a work item for a read-only question or incidental edit.
|
|
30
|
+
|
|
31
|
+
## Plan threshold
|
|
32
|
+
|
|
33
|
+
Use `plan` before implementation when the work has multiple dependent phases,
|
|
34
|
+
meaningful design choices, risky migration behavior, cross-component effects,
|
|
35
|
+
or a validation strategy that should survive the current conversation.
|
|
36
|
+
|
|
37
|
+
Skip a durable plan for changes that are local, obvious, reversible, and easy to
|
|
38
|
+
validate. Existing user-approved plans do not need another plan.
|
|
39
|
+
|
|
40
|
+
## Work checkpoint
|
|
41
|
+
|
|
42
|
+
- Use `research-codebase` or `research-web` only when the findings should remain
|
|
43
|
+
durable evidence.
|
|
44
|
+
- Use `decision` when an accepted choice and its rationale constrain later work.
|
|
45
|
+
- Use `document` for a durable synthesized reference.
|
|
46
|
+
- Never copy semantic write logic into an orchestration hook or adapter.
|
|
47
|
+
|
|
48
|
+
## Closure checkpoint
|
|
49
|
+
|
|
50
|
+
Before the final response:
|
|
51
|
+
|
|
52
|
+
1. Decide whether meaningful journal state changed.
|
|
53
|
+
2. If it did, use `journal` once after validation and reference supporting
|
|
54
|
+
entries actually used.
|
|
55
|
+
3. Check the latest spine entry before writing. Update or skip when a new entry
|
|
56
|
+
would describe the same completed outcome.
|
|
57
|
+
4. If validation failed, record the accurate partial or blocked state rather
|
|
58
|
+
than claiming completion.
|
|
59
|
+
5. End the response with one status marker defined in root `AGENTS.md`.
|
|
60
|
+
|
|
61
|
+
`closed` requires a repository-relative path to an existing spine entry.
|
|
62
|
+
`not-needed` means the task was read-only or too small to change durable state.
|
|
63
|
+
`off` means the user explicitly opted out for the request.
|
|
64
|
+
|
|
65
|
+
## Hook boundary
|
|
66
|
+
|
|
67
|
+
Harness hooks may:
|
|
68
|
+
|
|
69
|
+
- inject these workflow reminders;
|
|
70
|
+
- report active-work metadata;
|
|
71
|
+
- validate the final status marker and referenced entry;
|
|
72
|
+
- request at most one additional closure pass.
|
|
73
|
+
|
|
74
|
+
Hooks must not create work items, plans, research, decisions, documents, or
|
|
75
|
+
journal entries. They must safely no-op when the journal is absent and must not
|
|
76
|
+
read unstable transcript formats to infer work.
|
|
77
|
+
|
|
78
|
+
## Failure behavior
|
|
79
|
+
|
|
80
|
+
- A hook failure warns but does not replace journal safety or state rules.
|
|
81
|
+
- A missing or invalid active state uses the bounded error from `STATE.md` when
|
|
82
|
+
a semantic write is required.
|
|
83
|
+
- An unsupported, disabled, or untrusted hook falls back to these instructions.
|
|
84
|
+
- The hook-provided `stop_hook_active` guard permits only one continuation pass.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Work Item Rules
|
|
2
|
+
|
|
3
|
+
Agent configuration lives under `.agents/`. Durable journal data lives under
|
|
4
|
+
`.journal/`. Skills MUST read rules from `.agents/rules/` and MUST NOT write
|
|
5
|
+
runtime work data into `.agents/`.
|
|
6
|
+
|
|
7
|
+
Read these rules together with:
|
|
8
|
+
|
|
9
|
+
- `.agents/rules/METADATA.md`
|
|
10
|
+
- `.agents/rules/LINKS.md`
|
|
11
|
+
- `.agents/rules/SAFETY.md`
|
|
12
|
+
- `.agents/rules/STATE.md`
|
|
13
|
+
|
|
14
|
+
## Work item layout
|
|
15
|
+
|
|
16
|
+
When creating a work item, create this structure:
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
.journal/work/YYYY-MM-DD-NN-work-name/
|
|
20
|
+
├── work.md
|
|
21
|
+
├── journal/
|
|
22
|
+
├── _research/
|
|
23
|
+
├── docs/
|
|
24
|
+
└── decisions/
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- `work.md`: canonical work-item metadata and a short human-readable overview
|
|
28
|
+
- `journal/`: chronological spine entries
|
|
29
|
+
- `_research/`: supporting codebase and web research
|
|
30
|
+
- `docs/`: supporting synthesized references
|
|
31
|
+
- `decisions/`: supporting standalone decisions
|
|
32
|
+
|
|
33
|
+
## Naming
|
|
34
|
+
|
|
35
|
+
- Folder format: `YYYY-MM-DD-NN-work-name`.
|
|
36
|
+
- Date is the UTC creation date.
|
|
37
|
+
- `NN` is the next two-digit sequence for work items created that day.
|
|
38
|
+
- `work-name` is lowercase kebab-case, descriptive, and unique.
|
|
39
|
+
- Avoid spaces and special characters other than hyphens.
|
|
40
|
+
- The folder name MUST equal `slug` in `work.md`.
|
|
41
|
+
|
|
42
|
+
Example: `2026-06-29-01-portable-journal-skill-improvements`.
|
|
43
|
+
|
|
44
|
+
## Initialization
|
|
45
|
+
|
|
46
|
+
`init-work` MUST:
|
|
47
|
+
|
|
48
|
+
1. Inspect existing work folders to select a non-conflicting daily sequence.
|
|
49
|
+
2. Create the complete directory structure above.
|
|
50
|
+
3. Resolve identity according to `METADATA.md`.
|
|
51
|
+
4. Generate one stable `wi_` UUIDv7.
|
|
52
|
+
5. Create `work.md` with all required work-item metadata.
|
|
53
|
+
6. Default to `status: active` and `visibility: local_only`.
|
|
54
|
+
7. Set `createdAt` and `updatedAt` to the same current UTC timestamp.
|
|
55
|
+
8. Set `.journal/state.json` to the new slug according to `STATE.md`.
|
|
56
|
+
|
|
57
|
+
Work items are projects, not repositories. A project MAY span many repositories,
|
|
58
|
+
branches, and working directories. Do not create a new work item merely because
|
|
59
|
+
the repository changed.
|
|
60
|
+
|
|
61
|
+
## Lifecycle
|
|
62
|
+
|
|
63
|
+
Allowed status values:
|
|
64
|
+
|
|
65
|
+
- `draft`: created but not yet meaningful active work
|
|
66
|
+
- `active`: current ongoing work
|
|
67
|
+
- `paused`: intentionally inactive but expected to resume
|
|
68
|
+
- `completed`: intended outcome achieved
|
|
69
|
+
- `archived`: retained history with no expected continuation
|
|
70
|
+
|
|
71
|
+
Status updates preserve `createdAt`, update `updatedAt`, and never alter `id` or
|
|
72
|
+
`slug`. Visibility changes require an explicit user request.
|
|
73
|
+
|
|
74
|
+
Only one work item is selected as active in `.journal/state.json`, but other work
|
|
75
|
+
items may remain in `active` status. Active selection and lifecycle status are
|
|
76
|
+
different concepts.
|
|
77
|
+
|
|
78
|
+
## Legacy work items
|
|
79
|
+
|
|
80
|
+
A historical folder without `work.md` remains readable. Resume and recall may
|
|
81
|
+
infer its slug and title from the folder name. Audit flags the missing file;
|
|
82
|
+
reconcile may create it using deterministic legacy timestamps and
|
|
83
|
+
`source`/identity rules without modifying existing entry bodies.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Journal Entry Rules
|
|
2
|
+
|
|
3
|
+
Read these rules together with:
|
|
4
|
+
|
|
5
|
+
- `.agents/rules/METADATA.md`
|
|
6
|
+
- `.agents/rules/LINKS.md`
|
|
7
|
+
- `.agents/rules/SAFETY.md`
|
|
8
|
+
|
|
9
|
+
Markdown remains the source of truth. Every new entry MUST contain valid
|
|
10
|
+
canonical frontmatter followed by a concise, human-readable body.
|
|
11
|
+
|
|
12
|
+
## Entry locations and roles
|
|
13
|
+
|
|
14
|
+
Role is derived from `entryType`; never store a separate `role` field.
|
|
15
|
+
|
|
16
|
+
| Directory | Entry types | Derived role |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| `journal/` | `plan`, `implementation`, `status`, `manual` | spine |
|
|
19
|
+
| `_research/` | `research` | supporting |
|
|
20
|
+
| `docs/` | `doc` | supporting |
|
|
21
|
+
| `decisions/` | `decision` | supporting |
|
|
22
|
+
|
|
23
|
+
Supporting entries stand alone and are linked from spine or other supporting
|
|
24
|
+
entries. They do not add independent steps to the project timeline.
|
|
25
|
+
|
|
26
|
+
## Filenames
|
|
27
|
+
|
|
28
|
+
- Spine: `YYYY-MM-DD-NN-brief-description.md`
|
|
29
|
+
- Codebase research: `YYYY-MM-DD-NN-codebase-topic.md`
|
|
30
|
+
- Web research: `YYYY-MM-DD-NN-web-topic.md`
|
|
31
|
+
- Documents: `YYYY-MM-DD-NN-topic.md`
|
|
32
|
+
- Decisions: `YYYY-MM-DD-NN-decision-topic.md`
|
|
33
|
+
|
|
34
|
+
`NN` is the next sequence within the destination directory for that UTC date.
|
|
35
|
+
Filenames are descriptive, lowercase kebab-case. Entry identity comes from the
|
|
36
|
+
stable `ent_` UUIDv7, not the filename or entry number.
|
|
37
|
+
|
|
38
|
+
## Required spine body
|
|
39
|
+
|
|
40
|
+
Every spine entry MUST include these sections, adapted to its entry type:
|
|
41
|
+
|
|
42
|
+
### Project Timeline
|
|
43
|
+
|
|
44
|
+
A chronological index of the work item's spine. Supporting entries appear only
|
|
45
|
+
as links from the relevant spine entry.
|
|
46
|
+
|
|
47
|
+
### Current State
|
|
48
|
+
|
|
49
|
+
- components and their working/partial/broken status
|
|
50
|
+
- validation status with real counts when available
|
|
51
|
+
- key files and their current purpose
|
|
52
|
+
|
|
53
|
+
For a plan entry, this may be named `Current State Analysis`.
|
|
54
|
+
|
|
55
|
+
### Work Done This Session
|
|
56
|
+
|
|
57
|
+
- specific work performed
|
|
58
|
+
- problems resolved
|
|
59
|
+
- decisions made and why
|
|
60
|
+
- deterministic evidence such as paths, commits, and validation results
|
|
61
|
+
|
|
62
|
+
For a plan entry, replace this with the feature overview, proposed design, and
|
|
63
|
+
phased implementation plan. Do not claim planned work was implemented.
|
|
64
|
+
|
|
65
|
+
### Next Steps
|
|
66
|
+
|
|
67
|
+
- concrete actions in priority order
|
|
68
|
+
- known blockers and unresolved questions
|
|
69
|
+
|
|
70
|
+
Include architecture notes only when architecture changed or the plan proposes
|
|
71
|
+
an architectural change. Keep diagrams small and useful.
|
|
72
|
+
|
|
73
|
+
## Type-specific content
|
|
74
|
+
|
|
75
|
+
- `plan`: problem, goals, non-goals, current state, design, phases, tests, edge
|
|
76
|
+
cases, open questions, and next steps
|
|
77
|
+
- `implementation`: changes, rationale, files, validation, commits, current state,
|
|
78
|
+
and next steps
|
|
79
|
+
- `status`: current state, evidence, blockers, and next steps; no invented work
|
|
80
|
+
- `manual`: user-supplied or exceptional spine note following the core sections
|
|
81
|
+
- `research`: question, summary, sources/key files, findings, and open questions
|
|
82
|
+
- `doc`: standalone synthesized reference with source links
|
|
83
|
+
- `decision`: context, options, decision, rationale, consequences, and supersession
|
|
84
|
+
|
|
85
|
+
## Frontmatter and links
|
|
86
|
+
|
|
87
|
+
- Follow `METADATA.md` exactly.
|
|
88
|
+
- New output MUST have a useful one- or two-sentence `summary`.
|
|
89
|
+
- Skill-created files use `source: manual`.
|
|
90
|
+
- Entry `workItemId` MUST match the active work item's `work.md`.
|
|
91
|
+
- Store outgoing typed links according to `LINKS.md`.
|
|
92
|
+
- A human-readable body citation may accompany a structured link but does not
|
|
93
|
+
replace it.
|
|
94
|
+
- On edits, preserve `id` and `createdAt`; update `updatedAt`.
|
|
95
|
+
|
|
96
|
+
## Project timeline and compaction
|
|
97
|
+
|
|
98
|
+
The latest spine entry is the primary retrieval index.
|
|
99
|
+
|
|
100
|
+
- Recent entries may be listed individually.
|
|
101
|
+
- Older entries may be summarized into chronological milestone ranges when the
|
|
102
|
+
timeline threatens the 100-200 line target.
|
|
103
|
+
- Keep decisions, regressions, releases, and major architecture changes visible
|
|
104
|
+
as individual milestones regardless of age.
|
|
105
|
+
- Preserve entry numbers as recorded, including historical forks or gaps.
|
|
106
|
+
- Never delete or rewrite original entries as part of compaction.
|
|
107
|
+
- The timeline must still let a reader locate detailed source entries.
|
|
108
|
+
|
|
109
|
+
## Evidence and safety
|
|
110
|
+
|
|
111
|
+
- Include real file paths, commit hashes, PRs, commands, and test counts when known.
|
|
112
|
+
- Do not fabricate line numbers, test results, commits, or links.
|
|
113
|
+
- Prefer summaries and diff statistics over full diffs.
|
|
114
|
+
- Truncate failure output to the smallest useful redacted excerpt.
|
|
115
|
+
- Follow `SAFETY.md` before every write.
|
|
116
|
+
|
|
117
|
+
## Legacy entries
|
|
118
|
+
|
|
119
|
+
Markdown without frontmatter remains valid historical input. Infer metadata
|
|
120
|
+
best-effort from its directory, filename, and first heading. Do not block resume
|
|
121
|
+
or recall. Audit reports missing metadata; reconcile may add it conservatively
|
|
122
|
+
without rewriting the body or renumbering the entry.
|
|
123
|
+
|
|
124
|
+
## Compactness
|
|
125
|
+
|
|
126
|
+
Target 100-200 lines for a spine entry. Supporting entries may be longer when
|
|
127
|
+
the topic requires it, but should synthesize rather than dump source material.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Entry Link Rules
|
|
2
|
+
|
|
3
|
+
Links form a directed graph between any two journal entries. The source entry owns
|
|
4
|
+
the outgoing link in its frontmatter; incoming links are derived by scanning all
|
|
5
|
+
entries.
|
|
6
|
+
|
|
7
|
+
Read `.agents/rules/METADATA.md` before creating or modifying links.
|
|
8
|
+
|
|
9
|
+
## Link shape
|
|
10
|
+
|
|
11
|
+
Store outgoing links in the source entry's frontmatter:
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
links:
|
|
15
|
+
- id: lnk_0197c4d2-7b20-7abc-8d2e-0123456789ab
|
|
16
|
+
fromEntryId: ent_0197c4d2-7b20-7abc-8d2e-0123456789ab
|
|
17
|
+
toEntryId: ent_0197c4d3-1010-7def-8abc-0123456789ab
|
|
18
|
+
relation: references
|
|
19
|
+
createdAt: "2026-06-29T12:05:00.000Z"
|
|
20
|
+
targetPath: ../_research/codebase-portable-journal-skill-improvements.md
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Canonical fields:
|
|
24
|
+
|
|
25
|
+
- `id`: stable `lnk_` UUIDv7
|
|
26
|
+
- `fromEntryId`: MUST equal the containing entry's `id`
|
|
27
|
+
- `toEntryId`: MUST equal the target entry's `id`
|
|
28
|
+
- `relation`: `references`, `supersedes`, or `relates_to`
|
|
29
|
+
- `createdAt`: canonical ISO-8601 UTC timestamp
|
|
30
|
+
|
|
31
|
+
Filesystem addition:
|
|
32
|
+
|
|
33
|
+
- `targetPath`: required relative path from the source file to the target file
|
|
34
|
+
|
|
35
|
+
Do not add `referencedBy`; it is derived from other entries' outgoing links.
|
|
36
|
+
|
|
37
|
+
## Relation semantics
|
|
38
|
+
|
|
39
|
+
- `references`: source uses or cites target for context. This is the default.
|
|
40
|
+
- `supersedes`: source replaces or rolls back target. Direction is newer to older.
|
|
41
|
+
- `relates_to`: loose association or evolution when neither other relation fits.
|
|
42
|
+
|
|
43
|
+
The entry endpoints carry what they are through `entryType`; the relation says
|
|
44
|
+
why they are connected. Any entry type may link to any other entry type.
|
|
45
|
+
|
|
46
|
+
## Invariants
|
|
47
|
+
|
|
48
|
+
- The tuple `(fromEntryId, toEntryId, relation)` MUST be unique.
|
|
49
|
+
- `fromEntryId` and `toEntryId` MUST resolve to existing entries.
|
|
50
|
+
- `targetPath` MUST resolve to the same entry as `toEntryId`.
|
|
51
|
+
- Paths MUST be relative, use `/`, and include the `.md` extension.
|
|
52
|
+
- IDs remain authoritative across renames; paths are the human/filesystem locator.
|
|
53
|
+
- A path-only Markdown citation does not replace the structured link.
|
|
54
|
+
- Do not create a reciprocal link unless it has independent directional meaning.
|
|
55
|
+
- Do not create self-links.
|
|
56
|
+
|
|
57
|
+
Cross-work-item links are allowed.
|
|
58
|
+
Use the correct relative path and target ID.
|
|
59
|
+
|
|
60
|
+
## Spine and supporting entries
|
|
61
|
+
|
|
62
|
+
The role is derived from `entryType`:
|
|
63
|
+
|
|
64
|
+
- Spine: `plan`, `implementation`, `status`, `manual`
|
|
65
|
+
- Supporting: `research`, `doc`, `decision`
|
|
66
|
+
|
|
67
|
+
A supporting entry is orphaned when no entry links to it. Audit should flag an
|
|
68
|
+
orphan. Reconcile may add a link only when the source entry is unambiguous.
|
|
69
|
+
|
|
70
|
+
Typical relationships:
|
|
71
|
+
|
|
72
|
+
- plan `references` research
|
|
73
|
+
- implementation `references` a doc
|
|
74
|
+
- implementation `references` a decision
|
|
75
|
+
- decision `references` the research supporting its rationale
|
|
76
|
+
- newer decision `supersedes` an older decision
|
|
77
|
+
- entries in an evolution chain `relates_to` one another
|
|
78
|
+
|
|
79
|
+
## Link maintenance
|
|
80
|
+
|
|
81
|
+
- Creating or removing a link updates the source entry's `updatedAt`.
|
|
82
|
+
- Renaming a target updates every matching `targetPath`, but preserves link and
|
|
83
|
+
entry IDs and `createdAt`.
|
|
84
|
+
- Removing an incorrect link removes only the link object. Never delete either
|
|
85
|
+
entry as a side effect.
|
|
86
|
+
- When a path is broken but `toEntryId` resolves uniquely, reconcile may repair
|
|
87
|
+
`targetPath`.
|
|
88
|
+
- When an ID or target is ambiguous, report it for human review.
|
|
89
|
+
- Duplicate entry numbers do not affect link identity and are never auto-fixed.
|
|
90
|
+
|
|
91
|
+
## Legacy references
|
|
92
|
+
|
|
93
|
+
For legacy files, body references to `_research/*.md`, `docs/*.md`,
|
|
94
|
+
`decisions/*.md`, or another journal entry are evidence of a potentially missing structured link. Audit
|
|
95
|
+
reports the discrepancy. Reconcile may create the link only after resolving one
|
|
96
|
+
unique target and confirming the relation from context.
|