jorgex-stack 1.0.24 → 1.0.26
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 +4 -2
- package/dist/cli.js +52 -19
- package/package.json +1 -1
- package/stack/agents/orchestrator.md +2 -2
- package/stack/skills/work-lifecycle/SKILL.md +14 -2
- package/stack/skills/work-lifecycle/references/plan-template.md +3 -1
- package/stack/system-prompt/AGENTS.md +1 -1
- package/stack/system-prompt/engram-protocol.md +2 -0
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Portable multi-agent harness: one configuration source — 15 agents, 17 skills,
|
|
|
9
9
|
Install and run via npm without cloning the repository:
|
|
10
10
|
|
|
11
11
|
```
|
|
12
|
-
pnpm dlx jorgex-stack install #
|
|
12
|
+
pnpm dlx jorgex-stack install # install runtimes; first OpenCode setup selects connected models
|
|
13
13
|
pnpm dlx jorgex-stack models # model picker by runtime and tier (strong/standard/cheap)
|
|
14
14
|
pnpm dlx jorgex-stack sync # alias of install (same idempotent apply)
|
|
15
15
|
pnpm dlx jorgex-stack doctor # checks that everything is healthy (Engram, drift, hooks, keys)
|
|
@@ -24,7 +24,7 @@ For development from a clone, run the same commands through `pnpm cli <command>`
|
|
|
24
24
|
|
|
25
25
|
Every command supports `--dry-run`, `--yes`, and `--target-dir <dir>` for testing without touching the real config. Writes create automatic backups and verify idempotency; merges into user config are surgical (marked markdown sections, JSON/TOML upserts), so user-owned content is never touched.
|
|
26
26
|
|
|
27
|
-
Runtime defaults are documented in [docs/references/permissions.md](docs/references/permissions.md) for permissions and [docs/references/models.md](docs/references/models.md) for model selection,
|
|
27
|
+
Runtime defaults are documented in [docs/references/permissions.md](docs/references/permissions.md) for permissions and [docs/references/models.md](docs/references/models.md) for provider-aware model selection, Codex tiers, and orchestrator inheritance. OpenCode has no provider defaults.
|
|
28
28
|
|
|
29
29
|
### Modes: Human and Programmatic
|
|
30
30
|
|
|
@@ -52,6 +52,8 @@ Flags:
|
|
|
52
52
|
|
|
53
53
|
This installs into all detected runtimes. To be explicit, add `--agents opencode,claude-code,codex` or a comma-separated subset. Always pass `--mode programmatic`; without `--mode`, `--yes` and non-TTY installs default to `human`.
|
|
54
54
|
|
|
55
|
+
OpenCode also requires an existing selection in `~/.jorgex-stack/model-map.json`; run `pnpm dlx jorgex-stack models --agents opencode` interactively once before a headless install.
|
|
56
|
+
|
|
55
57
|
- `--mode human` cannot be combined with `--subagent-concurrency`.
|
|
56
58
|
- Without `--mode`, the first run asks interactively; `--yes`, non-TTY, and `--target-dir` default to `human`.
|
|
57
59
|
- `pnpm dlx jorgex-stack sync` reuses the saved mode; pass `--mode` to change and save the preference.
|
package/dist/cli.js
CHANGED
|
@@ -178,11 +178,6 @@ function resolveAgentModel(models, agentName, tier) {
|
|
|
178
178
|
};
|
|
179
179
|
}
|
|
180
180
|
var DEFAULT_MODEL_MAP = {
|
|
181
|
-
opencode: {
|
|
182
|
-
strong: { model: "openai/gpt-5.6-terra", variant: "xhigh" },
|
|
183
|
-
standard: { model: "openai/gpt-5.6-terra", variant: "xhigh" },
|
|
184
|
-
cheap: { model: "openai/gpt-5.6-luna", variant: "medium" }
|
|
185
|
-
},
|
|
186
181
|
"claude-code": {
|
|
187
182
|
strong: { model: "fable" },
|
|
188
183
|
standard: { model: "sonnet" },
|
|
@@ -209,7 +204,7 @@ function loadModelMap() {
|
|
|
209
204
|
} catch {
|
|
210
205
|
return DEFAULT_MODEL_MAP;
|
|
211
206
|
}
|
|
212
|
-
const merged = {};
|
|
207
|
+
const merged = { ...fromDisk };
|
|
213
208
|
for (const id of Object.keys(DEFAULT_MODEL_MAP)) {
|
|
214
209
|
merged[id] = { ...DEFAULT_MODEL_MAP[id], ...fromDisk[id] ?? {} };
|
|
215
210
|
}
|
|
@@ -1649,7 +1644,8 @@ async function runInstall(opts) {
|
|
|
1649
1644
|
}
|
|
1650
1645
|
const models = modelMap[id];
|
|
1651
1646
|
if (!models) {
|
|
1652
|
-
p.log.
|
|
1647
|
+
p.log.error(`${adapter.name}: sin modelos seleccionados \u2014 ejecuta 'jorgex-stack models --agents ${id}'.`);
|
|
1648
|
+
exitCode = 1;
|
|
1653
1649
|
continue;
|
|
1654
1650
|
}
|
|
1655
1651
|
const ctx = {
|
|
@@ -2867,8 +2863,12 @@ function agentsByTier() {
|
|
|
2867
2863
|
}
|
|
2868
2864
|
return grouped;
|
|
2869
2865
|
}
|
|
2866
|
+
function isCompleteRuntimeModelMap(models) {
|
|
2867
|
+
return TIERS.every((tier) => models[tier]?.model);
|
|
2868
|
+
}
|
|
2870
2869
|
async function askModel(det, subject, current) {
|
|
2871
2870
|
if (det.id === "codex") {
|
|
2871
|
+
if (!current) throw new Error("Codex requiere un model-map base.");
|
|
2872
2872
|
const effort = await p5.select({
|
|
2873
2873
|
message: `${det.name} \xB7 ${subject} \u2014 reasoning effort`,
|
|
2874
2874
|
options: EFFORTS.map((v) => ({ value: v, label: v })),
|
|
@@ -2902,16 +2902,18 @@ async function askModel(det, subject, current) {
|
|
|
2902
2902
|
}
|
|
2903
2903
|
const optionsList = det.options;
|
|
2904
2904
|
const options = optionsList.map((m) => ({ value: m, label: m }));
|
|
2905
|
-
if (!optionsList.includes(current.model))
|
|
2905
|
+
if (current && !optionsList.includes(current.model)) {
|
|
2906
|
+
options.unshift({ value: current.model, label: `${current.model} (actual)` });
|
|
2907
|
+
}
|
|
2906
2908
|
const choice = await p5.select({
|
|
2907
2909
|
message: `${det.name} \xB7 ${subject} \u2014 modelo`,
|
|
2908
2910
|
options,
|
|
2909
|
-
initialValue: current
|
|
2911
|
+
initialValue: current?.model,
|
|
2910
2912
|
maxItems: 12
|
|
2911
2913
|
});
|
|
2912
2914
|
if (p5.isCancel(choice)) return CANCEL;
|
|
2913
2915
|
if (det.id === "claude-code") return { model: choice };
|
|
2914
|
-
const keptCurrent = choice === current.model && current.variant ? current.variant : null;
|
|
2916
|
+
const keptCurrent = current && choice === current.model && current.variant ? current.variant : null;
|
|
2915
2917
|
const variant = await p5.select({
|
|
2916
2918
|
message: `${det.name} \xB7 ${subject} \u2014 reasoning effort (variant; solo si el modelo lo soporta)`,
|
|
2917
2919
|
options: [
|
|
@@ -2926,12 +2928,16 @@ async function askModel(det, subject, current) {
|
|
|
2926
2928
|
}
|
|
2927
2929
|
async function runModelsPicker(opts) {
|
|
2928
2930
|
const file = ensureModelMapFile();
|
|
2931
|
+
const map = loadModelMap();
|
|
2929
2932
|
if (opts.yes || !process.stdout.isTTY) {
|
|
2930
|
-
|
|
2933
|
+
if (opts.runtimes.includes("opencode") && !map.opencode) {
|
|
2934
|
+
console.error("OpenCode requiere selecci\xF3n interactiva desde los proveedores conectados; ejecuta 'models --agents opencode' sin --yes.");
|
|
2935
|
+
return 1;
|
|
2936
|
+
}
|
|
2937
|
+
console.log(`Model-map en ${file}. Ed\xEDtalo o ejecuta 'models' sin --yes para el picker.`);
|
|
2931
2938
|
return 0;
|
|
2932
2939
|
}
|
|
2933
2940
|
p5.intro("jorgex-stack models \u2014 modelos por tier o por subagente");
|
|
2934
|
-
const map = loadModelMap();
|
|
2935
2941
|
const grouped = agentsByTier();
|
|
2936
2942
|
const tierLine = (tier) => grouped[tier].join(", ");
|
|
2937
2943
|
const agentCount = TIERS.reduce((n, tier) => n + grouped[tier].length, 0);
|
|
@@ -2960,7 +2966,10 @@ async function runModelsPicker(opts) {
|
|
|
2960
2966
|
p5.log.warn("OpenCode: no se pudo listar `opencode models` \u2014 se mantiene la selecci\xF3n actual.");
|
|
2961
2967
|
continue;
|
|
2962
2968
|
}
|
|
2963
|
-
const
|
|
2969
|
+
const existingRuntimeMap = map[det.id];
|
|
2970
|
+
const runtimeMap = {
|
|
2971
|
+
...existingRuntimeMap
|
|
2972
|
+
};
|
|
2964
2973
|
p5.log.message(
|
|
2965
2974
|
`${det.name} \u2014 tiers y sus subagentes:
|
|
2966
2975
|
strong \u2192 ${tierLine("strong")}
|
|
@@ -2978,7 +2987,7 @@ async function runModelsPicker(opts) {
|
|
|
2978
2987
|
if (p5.isCancel(mode)) return cancelled();
|
|
2979
2988
|
if (mode === "tier") {
|
|
2980
2989
|
for (const tier of TIERS) {
|
|
2981
|
-
const asked = await askModel(det, `tier ${tier} (${tierLine(tier)})`,
|
|
2990
|
+
const asked = await askModel(det, `tier ${tier} (${tierLine(tier)})`, existingRuntimeMap?.[tier]);
|
|
2982
2991
|
if (asked === CANCEL) return cancelled();
|
|
2983
2992
|
runtimeMap[tier] = asked;
|
|
2984
2993
|
}
|
|
@@ -2991,10 +3000,15 @@ async function runModelsPicker(opts) {
|
|
|
2991
3000
|
} else {
|
|
2992
3001
|
const overrides = { ...runtimeMap.overrides ?? {} };
|
|
2993
3002
|
for (const tier of TIERS) {
|
|
2994
|
-
|
|
3003
|
+
let base = runtimeMap[tier];
|
|
2995
3004
|
for (const name of grouped[tier]) {
|
|
2996
|
-
const
|
|
3005
|
+
const current = existingRuntimeMap ? resolveAgentModel(existingRuntimeMap, name, tier) : void 0;
|
|
3006
|
+
const asked = await askModel(det, `${name} (tier ${tier})`, current);
|
|
2997
3007
|
if (asked === CANCEL) return cancelled();
|
|
3008
|
+
if (!base) {
|
|
3009
|
+
base = asked;
|
|
3010
|
+
runtimeMap[tier] = base;
|
|
3011
|
+
}
|
|
2998
3012
|
const sameAsTier = asked.model === base.model && (asked.variant ?? "") === (base.variant ?? "");
|
|
2999
3013
|
if (sameAsTier) {
|
|
3000
3014
|
delete overrides[name];
|
|
@@ -3009,6 +3023,9 @@ async function runModelsPicker(opts) {
|
|
|
3009
3023
|
if (Object.keys(overrides).length > 0) runtimeMap.overrides = overrides;
|
|
3010
3024
|
else delete runtimeMap.overrides;
|
|
3011
3025
|
}
|
|
3026
|
+
if (!isCompleteRuntimeModelMap(runtimeMap)) {
|
|
3027
|
+
throw new Error(`${det.name}: selecci\xF3n de modelos incompleta.`);
|
|
3028
|
+
}
|
|
3012
3029
|
map[det.id] = runtimeMap;
|
|
3013
3030
|
}
|
|
3014
3031
|
writeText(file, JSON.stringify(map, null, 2) + "\n");
|
|
@@ -3052,6 +3069,18 @@ function readPackageMetadata() {
|
|
|
3052
3069
|
// src/cli.ts
|
|
3053
3070
|
var VERSION = readPackageVersion();
|
|
3054
3071
|
var COMMANDS = ["install", "sync", "models", "update", "doctor", "restore", "uninstall"];
|
|
3072
|
+
async function ensureOpenCodeModelsForInstall(command, flags, runtimes) {
|
|
3073
|
+
if (!runtimes.includes("opencode") || loadModelMap().opencode) return true;
|
|
3074
|
+
const canPrompt = command === "install" && !flags.yes && !flags.dryRun && process.stdout.isTTY;
|
|
3075
|
+
if (canPrompt) {
|
|
3076
|
+
const code = await runModelsPicker({ yes: false, runtimes: ["opencode"] });
|
|
3077
|
+
if (code === 0 && loadModelMap().opencode) return true;
|
|
3078
|
+
}
|
|
3079
|
+
console.error(
|
|
3080
|
+
"OpenCode no tiene modelos configurados. Ejecuta 'jorgex-stack models --agents opencode' de forma interactiva antes de install/sync."
|
|
3081
|
+
);
|
|
3082
|
+
return false;
|
|
3083
|
+
}
|
|
3055
3084
|
function parseFlags(args) {
|
|
3056
3085
|
const flags = {
|
|
3057
3086
|
agents: [],
|
|
@@ -3204,9 +3233,9 @@ function printHelp() {
|
|
|
3204
3233
|
Uso: pnpm dlx jorgex-stack [comando] [opciones]
|
|
3205
3234
|
|
|
3206
3235
|
Comandos:
|
|
3207
|
-
install Instala el stack
|
|
3208
|
-
sync Re-aplica la config (idempotente;
|
|
3209
|
-
models Picker
|
|
3236
|
+
install Instala el stack; OpenCode fresh exige elegir modelos conectados
|
|
3237
|
+
sync Re-aplica la config y el model-map existente (idempotente; sin picker)
|
|
3238
|
+
models Picker por tier o subagente (OpenCode: 'opencode models' en vivo)
|
|
3210
3239
|
update --check: compara stack/Engram/skills con sus upstreams
|
|
3211
3240
|
doctor Estado: Engram, drift de config, hooks de Codex, key de context7
|
|
3212
3241
|
restore --list para ver backups \xB7 'restore <id>' para restaurar
|
|
@@ -3266,6 +3295,10 @@ Flags disponibles: jorgex-stack --help`
|
|
|
3266
3295
|
process.exitCode = 1;
|
|
3267
3296
|
return;
|
|
3268
3297
|
}
|
|
3298
|
+
if (!await ensureOpenCodeModelsForInstall(command, flags, runtimes)) {
|
|
3299
|
+
process.exitCode = 1;
|
|
3300
|
+
return;
|
|
3301
|
+
}
|
|
3269
3302
|
process.exitCode = await runInstall({ runtimes, targetDir: flags.targetDir, dryRun: flags.dryRun, yes: flags.yes, mode });
|
|
3270
3303
|
return;
|
|
3271
3304
|
}
|
package/package.json
CHANGED
|
@@ -99,7 +99,7 @@ The `work-lifecycle` skill is the single source of this flow. Summary — every
|
|
|
99
99
|
- `work/{name}/` (gitignored, exists only while the work is in progress) holds the human-reviewed artifacts: `PRD.md` and `plan.md`. They stay resident across intermediate PR merges; `plan.md` is the ONLY task status board — flip statuses with surgical edits; don't re-read the whole plan after every task (re-read it on resume).
|
|
100
100
|
- The full spec of each atomic task → Engram, one `mem_save` per task under `work/{name}/task/{NN}`. When you delegate a task, pass the subagent its topic_key + title — never the task content inline; it retrieves the spec itself.
|
|
101
101
|
- Phase outcomes, decisions and PR checkpoints → Engram under `work/{name}/{phase}` and `work/{name}/pr/{NN}`; tell each subagent which topic_key to use for its saves.
|
|
102
|
-
- Pending work → the project's single `work/backlog` topic_key
|
|
102
|
+
- Pending work → the project's single `work/backlog` topic_key, or issues (`to-issues`) if the project uses a tracker. Never a TODOs folder. For Engram, you are the **single writer**: before every change, retrieve the exact observation with `mem_get_observation`, preserve unrelated entries, send the complete content with `mem_update`, then read it again to verify. Never write it concurrently or use a blind topic-key upsert. Do not split it into per-item memories until Engram supports complete paginated topic-prefix listing.
|
|
103
103
|
- On final close: `mem_save` the outcome under `work/{name}/done`, move the PRD to the project's docs only if it has lasting documentation value, then delete `work/{name}/`. `work/{name}/done` is only for the last PR / final outcome. History is memory + git.
|
|
104
104
|
|
|
105
105
|
## Delegation map
|
|
@@ -173,7 +173,7 @@ When the plan is fully applied and VERIFY passes:
|
|
|
173
173
|
- **Critical Issues (must fix)**: apply ALL of them — the PR must not reach merge with these open.
|
|
174
174
|
- **Important Improvements (should fix)**: apply the ones worth doing now, at your judgment.
|
|
175
175
|
- **Suggestions (nice to have)**: apply only if trivial and safe.
|
|
176
|
-
3. Every finding you decide NOT to apply now goes to the project's `work/backlog` single topic_key — one line each: what + why deferred.
|
|
176
|
+
3. Every finding you decide NOT to apply now goes to the project's `work/backlog` single topic_key — one line each: what + why deferred. Apply the safe serialized backlog protocol above; subagents only return candidate lines.
|
|
177
177
|
4. For what you DO apply: add the new tasks to plan.md and one `mem_save` per task spec, execute them as in EXECUTE, re-verify, and push the fixes to the PR branch.
|
|
178
178
|
5. The review fires once per PR creation — pushing fixes does not re-trigger it. Re-run `/xreview` only if the fixes were large.
|
|
179
179
|
|
|
@@ -21,7 +21,7 @@ Every piece of work gets a **canonical kebab-case name** when it starts (e.g. `c
|
|
|
21
21
|
| PR checkpoint outcome | Engram `work/{name}/pr/{NN}` | Intermediate PR merge record |
|
|
22
22
|
| Phase outcomes, decisions, findings | Engram `work/{name}/{phase}` | History — must survive the folder and compactions |
|
|
23
23
|
| Final outcome | Engram `work/{name}/done` | Permanent record of what shipped after the last PR |
|
|
24
|
-
| Pending / backlog items | Engram `work/backlog` — ONE key per project | All pending ideas in
|
|
24
|
+
| Pending / backlog items | Engram `work/backlog` — ONE key per project | All pending ideas in one serialized list |
|
|
25
25
|
|
|
26
26
|
`work/` is **scaffolding, not product**: add it to the project's `.gitignore`. It contains ONLY work in progress — an empty `work/` means nothing is half-done. No `1-TODOs/`, no `3-finalized/`, no phase subfolders.
|
|
27
27
|
|
|
@@ -51,7 +51,18 @@ When presenting the PRD or the plan for human review on non-trivial work, OFFER
|
|
|
51
51
|
|
|
52
52
|
## Pending work (backlog)
|
|
53
53
|
|
|
54
|
-
All pending or future work of a project lives under the SINGLE topic_key `work/backlog
|
|
54
|
+
All pending or future work of a project lives under the SINGLE topic_key `work/backlog`: one list, each item a short title + one-liner. Never one topic_key per idea, never a TODOs folder. When an item starts, it graduates: remove it from the backlog and create its `work/{name}/`. Review findings deliberately NOT applied also land here, one line each (what + why deferred).
|
|
55
|
+
|
|
56
|
+
### Safe backlog mutation
|
|
57
|
+
|
|
58
|
+
Engram replaces an observation's complete content on both `mem_update` and a `mem_save` topic-key upsert. Until Engram offers complete, paginated listing by topic-key prefix, use this serialized protocol for every backlog add, edit or removal:
|
|
59
|
+
|
|
60
|
+
1. The active coordinator/orchestrator is the **single writer**. Subagents return candidate backlog lines; they never mutate `work/backlog` themselves. Never run backlog writes concurrently.
|
|
61
|
+
2. Find the exact `work/backlog` observation, then call `mem_get_observation` to read its full, untruncated content. If it does not exist, create it once with `mem_save`.
|
|
62
|
+
3. Change only the intended lines while preserving every unrelated entry, then call `mem_update` on that exact observation ID with the **complete content**. Never send only the delta and never use a blind `mem_save` upsert for an existing backlog.
|
|
63
|
+
4. Call `mem_get_observation` again and **verify** both the intended change and the preserved entries.
|
|
64
|
+
|
|
65
|
+
Engram has no atomic append or compare-and-swap, so concurrent writers can still lose data even if both read first. Do not split items into `work/backlog/{slug}` yet: `mem_search` is capped and has no paginated topic-prefix listing, so older active items could become invisible. Once that capability exists, one observation per item is the preferred migration. If the project has an issue tracker, use issues instead now and do not keep an Engram backlog too.
|
|
55
66
|
|
|
56
67
|
If the project manages work through an issue tracker, issues (`to-issues`) take this role instead — don't keep both.
|
|
57
68
|
|
|
@@ -91,6 +102,7 @@ For single-PR work, the PR checkpoint and final work close happen together: one
|
|
|
91
102
|
- Don't mix several distinct pieces of work under the same name/topic_key.
|
|
92
103
|
- Same evolving phase → same topic_key (upsert). Different phases and different tasks must not overwrite each other.
|
|
93
104
|
- Never persist the same artifact in two homes — no file + memory copies, no hybrid writes.
|
|
105
|
+
- Treat `work/backlog` as the exceptional serialized list described above; ordinary topic-key upserts are not a safe substitute for its read-modify-write protocol.
|
|
94
106
|
|
|
95
107
|
## Legacy `work/` folders
|
|
96
108
|
|
|
@@ -161,7 +161,7 @@ Status, wave and dependencies live in the plan.md table (single home) — do NOT
|
|
|
161
161
|
|
|
162
162
|
## Backlog entry — Template (`work/backlog`)
|
|
163
163
|
|
|
164
|
-
ONE observation per project holds every pending idea (topic_key `work/backlog
|
|
164
|
+
ONE observation per project holds every pending idea (topic_key `work/backlog`). Each item is just:
|
|
165
165
|
|
|
166
166
|
```markdown
|
|
167
167
|
- **[short title]** — [one-line description of the idea and its value]
|
|
@@ -169,6 +169,8 @@ ONE observation per project holds every pending idea (topic_key `work/backlog`,
|
|
|
169
169
|
|
|
170
170
|
When an item starts, remove it from this list and create its `work/[name]/`.
|
|
171
171
|
|
|
172
|
+
Mutation is serialized: the coordinator is the single writer, reads the exact observation with `mem_get_observation`, preserves all unrelated lines, sends the complete content through `mem_update`, then reads it again to verify. Never send a delta or use a blind topic-key upsert. See **Safe backlog mutation** in the parent skill for the full protocol and the current reason not to use one observation per item.
|
|
173
|
+
|
|
172
174
|
---
|
|
173
175
|
|
|
174
176
|
## Task creation rules
|
|
@@ -93,7 +93,7 @@ Every piece of information about a piece of work has exactly ONE home — never
|
|
|
93
93
|
- In-progress work lives in `work/{name}/` (gitignored): `PRD.md` + `plan.md`. They stay there across intermediate PR merges; `plan.md` is the ONLY task status board — update statuses with surgical edits. An empty `work/` means nothing is half-done.
|
|
94
94
|
- Execution worktrees and their branches always use the same name. Resolve the root with `git rev-parse --show-toplevel`, ensure `worktrees/` is ignored in the repo-local `.git/info/exclude`, then create/use `<project-root>/worktrees/<canonical-name>` for single-PR work or `<project-root>/worktrees/<canonical-name>-prNN` for multi-PR checkpoints; never create worktrees next to the repo, in the repo root, under `work/`, or in external temp/shared folders.
|
|
95
95
|
- Full task specs, phase outcomes, PR checkpoints and history live in Engram: `work/{name}/task/{NN}`, `work/{name}/{phase}`, `work/{name}/pr/{NN}`, `work/{name}/done`. Subagents receive a topic_key + title, never the task content inline.
|
|
96
|
-
- Pending work: the project's single `work/backlog` topic_key
|
|
96
|
+
- Pending work: the project's single `work/backlog` topic_key, or issues (`to-issues`) if the project uses a tracker. Never a TODOs folder. The coordinator/orchestrator is its **single writer**: retrieve the exact observation with `mem_get_observation`, preserve unrelated entries, send the complete content with `mem_update`, then read it again to verify; never mutate it concurrently or use a blind topic-key upsert. Do not split it into one memory per item until Engram supports complete paginated topic-prefix listing.
|
|
97
97
|
- On intermediate PR merge: save the checkpoint under `work/{name}/pr/{NN}` and keep `work/{name}/` alive for the remaining PRs.
|
|
98
98
|
- On final close: save the outcome under `work/{name}/done`, move the PRD to the project's docs only if it has lasting value, then delete `work/{name}/`. `work/{name}/done` is the final outcome only. History is memory + git — no archive folders.
|
|
99
99
|
|
|
@@ -42,6 +42,8 @@ Use the `engram` subagent for non-trivial memory reads — it filters and return
|
|
|
42
42
|
|
|
43
43
|
Work tracking follows the `work-lifecycle` skill: `work/{name}/plan.md` (file) is the only status board; memory holds the task specs (`work/{name}/task/{NN}`), phase outcomes (`work/{name}/{phase}`), PR checkpoints (`work/{name}/pr/{NN}`), and the final outcome in `work/{name}/done` only after the last PR; the project backlog stays under the single key `work/backlog`. Subagents retrieve their task by the topic_key the orchestrator passes them and save their phase outcome under the topic_key they were given BEFORE their final report.
|
|
44
44
|
|
|
45
|
+
`work/backlog` has a stricter mutation rule because Engram replaces complete content rather than applying a patch. The coordinator/orchestrator is the **single writer**; subagents only return candidates. Before every add, edit or removal, locate the exact observation and call `mem_get_observation`; preserve all unrelated entries, pass the complete content to `mem_update`, then read it again to verify. Never write it concurrently and never use a blind `mem_save` upsert. Separate `work/backlog/{slug}` memories are not safe yet because Engram lacks complete paginated topic-prefix listing; use tracker issues instead when available.
|
|
46
|
+
|
|
45
47
|
## Before ending a session
|
|
46
48
|
|
|
47
49
|
Call `mem_session_summary` with: Goal, Instructions, Discoveries, Accomplished, Next Steps, Relevant Files. This is NOT optional — without it the next session starts blind.
|