aimship 0.1.0-preview.4 → 0.1.0-preview.7

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.
Files changed (43) hide show
  1. package/CONTRIBUTING.md +22 -0
  2. package/README.md +68 -23
  3. package/README.zh-CN.md +69 -24
  4. package/docs/cli.md +52 -32
  5. package/docs/examples.md +5 -0
  6. package/docs/guide.md +248 -30
  7. package/docs/guide.zh-CN.md +277 -0
  8. package/docs/installation.md +17 -110
  9. package/lib/cli.mjs +28 -16
  10. package/lib/documents.mjs +155 -0
  11. package/lib/migrate.mjs +77 -0
  12. package/lib/state.mjs +87 -21
  13. package/lib/view.mjs +69 -0
  14. package/package.json +3 -2
  15. package/skills/aimship/SKILL.md +1 -1
  16. package/skills/aimship/references/artifact-map.json +163 -0
  17. package/skills/aimship/references/core.md +8 -4
  18. package/skills/aimship/references/document-patterns.md +30 -10
  19. package/skills/aimship/references/hosts.md +4 -2
  20. package/skills/aimship/references/recovery.md +8 -0
  21. package/skills/aimship/references/sources-and-changes.md +6 -0
  22. package/skills/aimship/references/stages/contract.md +4 -0
  23. package/skills/aimship/references/stages/diagnose.md +6 -0
  24. package/skills/aimship/references/stages/frame.md +4 -0
  25. package/skills/aimship/references/stages/handoff.md +4 -0
  26. package/skills/aimship/references/stages/implement.md +6 -0
  27. package/skills/aimship/references/stages/plan.md +5 -1
  28. package/skills/aimship/references/stages/prototype.md +4 -0
  29. package/skills/aimship/references/stages/requirements.md +4 -0
  30. package/skills/aimship/references/stages/review.md +4 -0
  31. package/skills/aimship/references/stages/scope.md +4 -0
  32. package/skills/aimship/references/stages/tech-design.md +6 -0
  33. package/skills/aimship/references/stages/test-design.md +5 -1
  34. package/skills/aimship/references/stages/verify.md +6 -0
  35. package/skills/aimship/references/templates/change-log.md +14 -0
  36. package/skills/aimship/references/templates/diagnosis.md +39 -0
  37. package/skills/aimship/references/templates/handoff.md +34 -0
  38. package/skills/aimship/references/templates/plan.md +34 -0
  39. package/skills/aimship/references/templates/prd.md +39 -0
  40. package/skills/aimship/references/templates/review.md +19 -0
  41. package/skills/aimship/references/templates/scope.md +39 -0
  42. package/skills/aimship/references/templates/tech-design.md +39 -0
  43. package/skills/aimship/references/templates/testing.md +34 -0
package/lib/state.mjs CHANGED
@@ -1,9 +1,11 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
+ import { inspectDocuments, documentChanges, anchors, preservedLegacyRuns } from './documents.mjs';
4
+ import { savedResult, viewStatus, generateView } from './view.mjs';
3
5
  import { randomUUID } from 'node:crypto';
4
6
  import { atomicWrite, exists, fail, inside, json, noSymlinks, readJSON, withLock } from './io.mjs';
5
7
 
6
- export const SCHEMA_VERSION = 1;
8
+ export const SCHEMA_VERSION = 2;
7
9
  export const STAGES = ['frame', 'requirements', 'prototype', 'scope', 'diagnose', 'tech-design', 'contract', 'plan', 'test-design', 'implement', 'review', 'verify', 'handoff'];
8
10
  const PROGRESS = ['planned', 'active', 'blocked', 'done'];
9
11
  const RESULTS = ['passed', 'failed', 'not-run', 'unknown', 'mixed'];
@@ -24,7 +26,21 @@ function unique(items, label) { requireThat(items.every(object), `${label} entri
24
26
 
25
27
  export function validateState(state) {
26
28
  requireThat(object(state), 'State must be an object.');
27
- if (state.schemaVersion !== SCHEMA_VERSION) fail('UNKNOWN_SCHEMA', `Schema ${state.schemaVersion} is read-only for this tool.`, 'Preserve the file and use a compatible tool or an explicit, reviewed migration.');
29
+ if (![1, SCHEMA_VERSION].includes(state.schemaVersion)) fail('UNKNOWN_SCHEMA', `Schema ${state.schemaVersion} is read-only for this tool.`, 'Preserve the file and use a compatible tool or an explicit, reviewed migration.');
30
+ if (state.schemaVersion === 2) {
31
+ requireThat(object(state.documentPolicy) && ['authored','adopted','editable','legacy'].includes(state.documentPolicy.authority), 'documentPolicy.authority is required.');
32
+ if (state.documentPolicy.authority !== 'legacy') {
33
+ const p = state.documentPolicy;
34
+ requireThat(typeof p.interaction === 'boolean' && typeof p.prototypeCovered === 'boolean' && ['none','http','other'].includes(p.interface) && Array.isArray(p.databases) && p.databases.every(x => typeof x === 'string' && /^[a-z0-9][a-z0-9-]*$/.test(x)) && new Set(p.databases).size === p.databases.length, 'Invalid documentPolicy conditions.');
35
+ requireThat(Array.isArray(state.documents), 'documents must be an array.');
36
+ const paths = new Set();
37
+ for (const d of state.documents) {
38
+ requireThat(object(d) && ['prd','scope','tech-design','plan','testing','review','diagnosis','handoff','change-log','prototype','contract','sql','source'].includes(d.kind) && ['new','reused'].includes(d.mode) && string(d.version), 'Invalid document registration.');
39
+ ref(d.path, 'documents.path'); requireThat(!d.path.includes('#') && !paths.has(d.path), 'Document paths must be unique and have no fragment.'); paths.add(d.path);
40
+ if (d.mode === 'reused') { ref(d.basisRef, 'documents.basisRef'); }
41
+ }
42
+ }
43
+ }
28
44
  requireThat(Number.isSafeInteger(state.revision) && state.revision >= 0, 'revision must be a nonnegative integer.');
29
45
  requireThat(string(state.deliveryId) && string(state.rulesVersion), 'deliveryId and rulesVersion are required.');
30
46
  requireThat(object(state.goal) && string(state.goal.summary) && string(state.goal.endpoint), 'goal.summary and goal.endpoint are required.');
@@ -60,16 +76,51 @@ export function validateState(state) {
60
76
  return state;
61
77
  }
62
78
 
63
- export function statePaths(directory) {
64
- const dir = path.resolve(directory);
65
- return { dir, statePath: path.join(dir, 'state.json'), backup: path.join(dir, 'state.previous.json'), lock: path.join(dir, '.state.lock') };
79
+ export function statePaths(directory, legacy = false) {
80
+ const dir = path.resolve(directory), internal = legacy ? dir : path.join(dir,'.aimship');
81
+ return { dir, internal, statePath:path.join(internal,'state.json'), backup:path.join(internal,'state.previous.json'), lock:path.join(internal,'.state.lock'), legacy };
82
+ }
83
+ export async function locateState(directory, writing = false) {
84
+ const modern=statePaths(directory), legacy=statePaths(directory,true);
85
+ const hasNew=await exists(modern.statePath), hasOld=await exists(legacy.statePath);
86
+ const journalPath=path.join(modern.internal,'migration.json');
87
+ const journal=await exists(journalPath) ? await readJSON(journalPath) : null;
88
+ if (hasNew && hasOld) {
89
+ if (writing) fail('LAYOUT_CONFLICT','Both state layouts exist.','Inspect both copies; resume a recorded migration explicitly.');
90
+ return {...modern,conflict:true,candidates:[legacy.statePath,modern.statePath],migration:journal};
91
+ }
92
+ if (writing && journal && journal.status !== 'complete') fail('MIGRATION_PENDING','Migration is incomplete.','Resume migrate with the original revision and compatibilityRef.');
93
+ return {...(hasOld || (!hasNew && await exists(legacy.backup)) ? legacy : modern),migration:journal};
66
94
  }
67
95
  export async function readState(directory) {
68
- const paths = statePaths(directory); const state = await readJSON(paths.statePath);
69
- requireThat(object(state), 'State must be an object.');
70
- const readOnly = state.schemaVersion !== SCHEMA_VERSION;
96
+ const paths=await locateState(directory); const state=await readJSON(paths.statePath);
97
+ requireThat(object(state),'State must be an object.');
98
+ const layoutMismatch=(state.schemaVersion===1 && !paths.legacy)||(state.schemaVersion===2 && paths.legacy);
99
+ const readOnly=paths.conflict || layoutMismatch || ![1,SCHEMA_VERSION].includes(state.schemaVersion);
71
100
  if (!readOnly) validateState(state);
72
- return { ...paths, state, readOnly };
101
+ return {...paths,state,readOnly,layoutMismatch,view:await viewStatus(directory,state),documentChanges:await documentChanges(directory,state)};
102
+ }
103
+ async function inspectForWrite(directory,state,previous) {
104
+ const inspected=await inspectDocuments(directory,state);
105
+ if (inspected.issues.length) fail('DOCUMENT_CHECK','Document structure or traceability needs repair.','Repair the specific documents; file presence is not semantic completion.',inspected);
106
+ // Historical migrated runs retain their old formats. New completed runs need resolvable results.
107
+ const legacyRuns=await preservedLegacyRuns(directory,state);
108
+ for (const run of state.stageRuns.filter(r=>r.progress==='done' && !legacyRuns.has(r.id))) {
109
+ if (previous?.stageRuns.some(r=>r.id===run.id && JSON.stringify(r)===JSON.stringify(run))) continue;
110
+ const results=await checkReferences(directory,{...state,goalRef:run.resultRef,next:{inputRefs:[]},stageRuns:[],currentRun:null});
111
+ if (results.some(r=>!['exists','not-checked'].includes(r.status))) fail('DOCUMENT_CHECK',`Cannot locate result for ${run.id}.`,'Fix the result reference.',{references:results});
112
+ }
113
+ state.documentDigests=inspected.digests;
114
+ state.retiredIdentities=[...new Set([...(previous?.retiredIdentities??[]),...inspected.retired??[]])];
115
+ state.identityHistory=[...new Set([...(previous?.identityHistory??[]),...inspected.identities])];
116
+ }
117
+ export async function renderState(directory,revision,rulesVersion,runtime,preserveEdits=false) {
118
+ const p=await locateState(directory,true);
119
+ return withLock(p.lock,async()=>{
120
+ const {state}=await readState(directory); validateState(state); checkRevision(state,revision); assertRules(state,rulesVersion,runtime);
121
+ if (p.legacy) fail('MIGRATION_REQUIRED','Migrate before rendering.','Use migrate, then adopt.');
122
+ return {revision:state.revision,stateSaved:false,view:await generateView(directory,state,preserveEdits)};
123
+ });
73
124
  }
74
125
  function checkRevision(state, revision) {
75
126
  if (!Number.isSafeInteger(revision) || revision !== state.revision) fail('REVISION_CONFLICT', `Expected revision ${revision}; current revision is ${state.revision}.`, 'Read state again and merge your intended changes; never retry with a guessed revision.', { expected: revision, actual: state.revision });
@@ -84,14 +135,15 @@ export function assertInstallationComplete(runtime) {
84
135
  export async function initState(directory, input, rulesVersion, runtime) {
85
136
  assertInstallationComplete(runtime);
86
137
  if (rulesVersion !== runtime.version || runtime.activeVersion !== runtime.version) fail('VERSION_MISMATCH', 'New delivery requires the active tool and rules version.', 'Bind the current installation and use its returned version.');
87
- const p = statePaths(directory); await noSymlinks(p.dir); await fs.mkdir(p.dir, { recursive: true });
138
+ const p = statePaths(directory); await noSymlinks(p.internal); await fs.mkdir(p.internal, { recursive: true });
139
+ if (await exists(path.join(p.dir,'state.json')) || await exists(path.join(p.dir,'state.previous.json')) || await exists(p.backup) || await exists(path.join(p.internal,'migration.json'))) fail('ALREADY_EXISTS','Existing delivery or recovery material found.','Read/resume the existing delivery.');
88
140
  return withLock(p.lock, async () => {
89
141
  if (await exists(p.statePath)) fail('ALREADY_EXISTS', 'A delivery already exists here.', 'Read/resume this delivery or choose a different folder.');
90
- const forbidden = ['schemaVersion', 'revision', 'deliveryId', 'rulesVersion', 'createdAt', 'updatedAt', 'goalHistory', 'rulesHistory'];
142
+ const forbidden = ['schemaVersion', 'revision', 'deliveryId', 'rulesVersion', 'createdAt', 'updatedAt', 'goalHistory', 'rulesHistory', 'documentDigests', 'identityHistory', 'retiredIdentities', 'legacyRunIds', 'writeResult'];
91
143
  requireThat(object(input) && !forbidden.some(x => Object.hasOwn(input, x)), 'init input must not set tool-managed identity/version fields.');
92
144
  const now = new Date().toISOString();
93
145
  const state = { cycle: 1, closureRefs: [], stageRuns: [], currentRun: null, openChanges: [], repositories: [], outcome: { goal: 'in-progress', acceptance: 'not-recorded' }, next: { action: 'Continue the confirmed arrangement.' }, ...input, schemaVersion: SCHEMA_VERSION, revision: 0, deliveryId: randomUUID(), rulesVersion, createdAt: now, updatedAt: now };
94
- validateState(state); await atomicWrite(p.statePath, json(state)); return state;
146
+ validateState(state); requireThat(state.documentPolicy.authority !== 'legacy', 'New deliveries cannot opt into legacy rules.'); await inspectForWrite(directory,state); await atomicWrite(p.statePath, json(state)); return savedResult(directory,state);
95
147
  });
96
148
  }
97
149
  function merge(base, patch) {
@@ -120,9 +172,12 @@ function retainHistory(previous, next) {
120
172
  }
121
173
  export async function updateState(directory, patch, revision, rulesVersion, runtime, mode = 'update') {
122
174
  assertInstallationComplete(runtime);
123
- const p = statePaths(directory);
175
+ const p = await locateState(directory,true);
124
176
  return withLock(p.lock, async () => {
125
- const { state: previous } = await readState(directory); validateState(previous); checkRevision(previous, revision);
177
+ const read = await readState(directory);
178
+ if (read.layoutMismatch) fail('LAYOUT_CONFLICT','Schema and layout disagree.','Inspect the original layout; do not write through an inconsistent path.');
179
+ const previous = read.state; validateState(previous); checkRevision(previous, revision);
180
+ if (previous.schemaVersion === 1 && mode !== 'checkpoint') fail('MIGRATION_REQUIRED','Legacy layout/schema must migrate before rule adoption or updates.','Use migrate with a compatibility assessment, then adopt.');
126
181
  requireThat(object(patch), 'Update input must be an object.');
127
182
  if (mode === 'checkpoint') {
128
183
  if (rulesVersion !== previous.rulesVersion || runtime.version !== previous.rulesVersion) fail('VERSION_MISMATCH', 'Checkpoint must use the rules already bound to this state.', 'Use the compatible old tool or preserve facts outside state and resume with the active tool.');
@@ -138,7 +193,7 @@ export async function updateState(directory, patch, revision, rulesVersion, runt
138
193
  } else {
139
194
  assertRules(previous, rulesVersion, runtime);
140
195
  }
141
- const immutable = ['schemaVersion', 'revision', 'deliveryId', 'rulesVersion', 'createdAt', 'updatedAt', 'goalHistory', 'rulesHistory'];
196
+ const immutable = ['schemaVersion', 'revision', 'deliveryId', 'rulesVersion', 'createdAt', 'updatedAt', 'goalHistory', 'rulesHistory', 'documentDigests', 'identityHistory', 'retiredIdentities', 'legacyRunIds', 'writeResult'];
142
197
  requireThat(!immutable.some(x => Object.hasOwn(patch, x)), 'Identity, revision, and historical metadata are tool-managed.');
143
198
  let next;
144
199
  if (mode === 'adopt') {
@@ -150,12 +205,15 @@ export async function updateState(directory, patch, revision, rulesVersion, runt
150
205
  requireThat(string(next.goal.decisionRef) && next.goal.decisionRef !== previous.goal.decisionRef, 'Goal revision requires a new explicit decisionRef; viewing results does not revise a goal.');
151
206
  next.goalHistory = [...(previous.goalHistory ?? []), { goal: previous.goal, goalRef: previous.goalRef, outcome: previous.outcome, revision: previous.revision }];
152
207
  }
208
+ requireThat(previous.documentPolicy?.authority === 'legacy' || next.documentPolicy?.authority !== 'legacy', 'Cannot downgrade a new delivery to legacy document rules.');
209
+ if (mode === 'update' && next.documentPolicy?.authority === 'legacy') requireThat(Object.keys(patch).every(k=>['next','checkpoint','repositories'].includes(k)), 'Reconcile legacy documents and set the current documentPolicy before advancing stages or outcomes.');
210
+ if (mode === 'update') await inspectForWrite(directory,next,previous);
153
211
  retainHistory(previous, next); next.revision = previous.revision + 1; next.updatedAt = new Date().toISOString();
154
- validateState(next); await atomicWrite(p.backup, json(previous)); await atomicWrite(p.statePath, json(next)); return next;
212
+ validateState(next); await atomicWrite(p.backup, json(previous)); await atomicWrite(p.statePath, json(next)); return next.schemaVersion === 2 ? savedResult(directory,next) : next;
155
213
  });
156
214
  }
157
215
  export async function recoverState(directory, revision, rulesVersion, runtime) {
158
- const p = statePaths(directory);
216
+ const p = await locateState(directory,true);
159
217
  return withLock(p.lock, async () => {
160
218
  const backup = await readJSON(p.backup); validateState(backup); checkRevision(backup, revision); assertRules(backup, rulesVersion, runtime);
161
219
  let corruptRevision = backup.revision + 1;
@@ -163,15 +221,15 @@ export async function recoverState(directory, revision, rulesVersion, runtime) {
163
221
  const raw = await fs.readFile(p.statePath, 'utf8');
164
222
  let parsed; try { parsed = JSON.parse(raw); } catch { /* Corruption is preserved below. */ }
165
223
  if (parsed) {
166
- if (parsed.schemaVersion !== SCHEMA_VERSION) fail('UNKNOWN_SCHEMA', 'Cannot recover over an unknown schema.', 'Use a compatible tool; preserve the newer state.');
224
+ if (parsed.schemaVersion !== backup.schemaVersion) fail('UNKNOWN_SCHEMA', 'Cannot recover over an unknown schema.', 'Use a compatible tool; preserve the newer state.');
167
225
  try { validateState(parsed); fail('STATE_VALID', 'Current state is valid; refusing rollback.', 'Use a normal revision-checked update to reconcile facts.'); }
168
226
  catch (e) { if (e.code !== 'INVALID_STATE') throw e; }
169
227
  if (Number.isSafeInteger(parsed.revision)) corruptRevision = Math.max(corruptRevision, parsed.revision);
170
228
  }
171
- await atomicWrite(path.join(p.dir, `state.corrupt.${randomUUID()}.json`), raw);
229
+ await atomicWrite(path.join(p.internal, `state.corrupt.${randomUUID()}.json`), raw);
172
230
  }
173
231
  const next = { ...backup, revision: corruptRevision + 1, updatedAt: new Date().toISOString(), recovery: { fromRevision: backup.revision, action: 'Inspect actual artifacts and unfinished effects before proceeding; nothing has been re-executed.' } };
174
- await atomicWrite(p.statePath, json(next)); return next;
232
+ await atomicWrite(p.statePath, json(next)); return next.schemaVersion === 2 ? savedResult(directory,next) : next;
175
233
  });
176
234
  }
177
235
  export async function checkReferences(directory, state, all = false) {
@@ -203,7 +261,15 @@ export async function checkReferences(directory, state, all = false) {
203
261
  try {
204
262
  const real = await fs.realpath(target); const realBase = await fs.realpath(base);
205
263
  if (!inside(realBase, real)) results.push({ ref: value, status: 'outside-root', path: target });
206
- else { await fs.access(real, fs.constants.R_OK); results.push({ ref: value, status: 'exists', path: target, fragmentChecked: false }); }
264
+ else {
265
+ await fs.access(real, fs.constants.R_OK);
266
+ const fragment=value.split('#')[1], doc=state.documents?.find(d=>d.path===value.split('#')[0]);
267
+ if (fragment && doc?.mode==='new' && /\.md$/.test(relative)) {
268
+ const ids=anchors(await fs.readFile(real,'utf8'));
269
+ results.push({ref:value,status:ids.filter(x=>x===fragment).length===1?'exists':'missing-fragment',path:target,fragmentChecked:true});
270
+ } else if (fragment) results.push({ref:value,status:'not-checked',path:target,fragmentChecked:false,reason:'Nonstandard/reused fragment requires host inspection and recorded basis.'});
271
+ else results.push({ref:value,status:'exists',path:target,fragmentChecked:false});
272
+ }
207
273
  } catch (e) { results.push({ ref: value, status: e.code === 'ENOENT' ? 'missing' : 'inaccessible', path: target, reason: e.code }); }
208
274
  }
209
275
  return results;
package/lib/view.mjs ADDED
@@ -0,0 +1,69 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { atomicWrite, exists, hash, json, readJSON, noSymlinks } from './io.mjs';
5
+ import { resolveRef } from './documents.mjs';
6
+
7
+ const paths = dir => ({ view: path.join(dir,'status.md'), meta: path.join(dir,'.aimship/view.json') });
8
+ export async function viewStatus(directory, state) {
9
+ if (state.schemaVersion !== 2) return { status:'legacy', recovery:'Explicitly migrate this delivery before generating its view.' };
10
+ const p=paths(directory);
11
+ try {
12
+ if (!await exists(p.view)) return { status:'missing', recovery:'Run render at the current revision.' };
13
+ await noSymlinks(p.view); await noSymlinks(p.meta);
14
+ if (!await exists(p.meta)) return { status:'modified', recovery:'No trusted generation digest; preserve edits with render --preserve-edits.' };
15
+ const meta=await readJSON(p.meta), raw=await fs.readFile(p.view,'utf8');
16
+ if (hash(raw)!==meta.hash) return { status:'modified', recovery:'Inspect the edits, then render --preserve-edits.' };
17
+ const current=meta.deliveryId===state.deliveryId && meta.revision===state.revision;
18
+ return { status:current?'current':'stale', revision:meta.revision, recovery:current?null:'Run render; do not replay the saved business update.' };
19
+ } catch(error) { return {status:'error',reason:error.code ?? error.message,recovery:'Inspect view/metadata paths; preserve untrusted content before render.'}; }
20
+ }
21
+ const cell = x => String(x ?? 'not recorded').replaceAll('|','\\|').replaceAll('\n',' ');
22
+ const link = ref => `[${ref.replaceAll('[','\\[').replaceAll(']','\\]')}](${ref.replaceAll(' ','%20').replaceAll('(','%28').replaceAll(')','%29')})`;
23
+ async function available(directory,state,ref) {
24
+ if (/^https?:\/\//.test(ref)) return true;
25
+ const p=resolveRef(directory,state,ref); return p && await exists(p);
26
+ }
27
+ export async function renderText(directory,state) {
28
+ const artifactLink=ref=> {
29
+ if (!ref.startsWith('repo:')) return link(ref);
30
+ const target=resolveRef(directory,state,ref);
31
+ const fragment=ref.includes('#')?'#'+ref.split('#')[1]:'';
32
+ return target ? `[${cell(ref)}](${encodeURI(target).replaceAll('(','%28').replaceAll(')','%29')}${fragment})` : cell(ref);
33
+ };
34
+ const lines=['# Delivery status','','Generated by Aimship. Edit formal documents and structured state, not this page.','', '## Goal and basis','', cell(state.goal.summary), '',`Endpoint: ${cell(state.goal.endpoint)}`,''];
35
+ if (await available(directory,state,state.goalRef)) lines.push(artifactLink(state.goalRef),'');
36
+ lines.push('## Associated engineering objects','','| ID | Actual checkout/worktree | Role |','| --- | --- | --- |');
37
+ for (const r of state.repositories) lines.push(`| ${cell(r.id)} | ${cell(r.path)} | ${cell(r.role)} |`);
38
+ if (!state.repositories.length) lines.push('| — | No repository registered | — |');
39
+ lines.push('','## Stage progress','','| Run / scope | Stage | Progress | Result | Applicability |','| --- | --- | --- | --- | --- |');
40
+ for (const r of state.stageRuns) lines.push(`| ${cell(r.id)} / ${cell(r.scope)} | ${r.stage} | ${r.progress} | ${cell(r.result)} | ${cell(r.applicability)} |`);
41
+ lines.push('','## Current work, blockers and next action','',`Current: ${cell(state.currentRun)}`, '',`Next: ${cell(state.next.action)}`);
42
+ for (const r of state.stageRuns.filter(r=>r.progress==='blocked')) lines.push(`- Blocked: ${cell(r.id)} — ${cell(r.scope)}`);
43
+ for (const c of state.openChanges.filter(c=>c.status==='open')) lines.push(`- Open change/question: ${link(c.ref)}`);
44
+ if (state.checkpoint) lines.push('',`Unfinished facts: ${cell(state.checkpoint.summary)}`);
45
+ lines.push('','## Artifacts and plan','');
46
+ const refs=new Set([...(state.documents??[]).map(d=>d.path),...state.stageRuns.flatMap(r=>[...r.outputRefs,r.resultRef].filter(Boolean)),...(state.next.inputRefs??[])]);
47
+ for (const ref of refs) if (await available(directory,state,ref)) lines.push(`- ${artifactLink(ref)}`);
48
+ lines.push('','## Independent outcomes and generation','',`Goal: ${state.outcome.goal}`,`Acceptance: ${state.outcome.acceptance}`,`Merge: ${state.outcome.mergeRef?link(state.outcome.mergeRef):'not recorded'}`,`Deployment: ${state.outcome.deploymentRef?link(state.outcome.deploymentRef):'not recorded'}`,'',`Delivery: ${state.deliveryId}`,`State revision: ${state.revision}`,`Rules: ${state.rulesVersion}`,`State saved: ${state.updatedAt}`,'','Matching revisions do not establish document semantics or authorization.','');
49
+ return lines.join('\n');
50
+ }
51
+ export async function generateView(directory,state,preserveEdits=false) {
52
+ const p=paths(directory); const diagnostic=await viewStatus(directory,state);
53
+ let preservedPath;
54
+ try {
55
+ if (['modified','error'].includes(diagnostic.status) && await exists(p.view)) {
56
+ if (!preserveEdits) return {...diagnostic,status:'protected'};
57
+ await noSymlinks(p.view);
58
+ preservedPath=path.join(directory,'.aimship',`status-edits-${randomUUID()}.md`);
59
+ await atomicWrite(preservedPath,await fs.readFile(p.view));
60
+ }
61
+ const text=await renderText(directory,state);
62
+ await atomicWrite(p.view,text);
63
+ await atomicWrite(p.meta,json({deliveryId:state.deliveryId,revision:state.revision,hash:hash(text)}));
64
+ return {status:'current',revision:state.revision,...(preservedPath?{preservedPath}:{})};
65
+ } catch(error) {return {status:'error',reason:error.code??error.message,...(preservedPath?{preservedPath}:{}),recovery:'State was saved. Repair the view paths, then render at the returned revision; do not replay update.'};}
66
+ }
67
+ export async function savedResult(directory,state) {
68
+ return {...state,writeResult:{stateSaved:true,revision:state.revision,view:await generateView(directory,state)}};
69
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aimship",
3
- "version": "0.1.0-preview.4",
3
+ "version": "0.1.0-preview.7",
4
4
  "description": "Explicit, goal-driven delivery workflow for coding agents",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,7 +23,8 @@
23
23
  "scripts": {
24
24
  "test": "node --test --test-concurrency=1",
25
25
  "check:package": "node scripts/check-package.mjs",
26
- "prepack": "node scripts/check-package.mjs"
26
+ "prepack": "node scripts/check-package.mjs",
27
+ "check:docs": "node scripts/check-docs.mjs"
27
28
  },
28
29
  "publishConfig": {
29
30
  "registry": "https://registry.npmjs.org",
@@ -14,6 +14,6 @@ Activate only when the user explicitly asks to start or resume Aimship. Ordinary
14
14
  4. Read the selected [stage reference](references/core.md#stage-references) when reaching it. Explain design choices and unresolved decisions, then continue authorized work. Only material missing facts or choices block their dependent work.
15
15
  5. At stable results and before a manual session switch, save the state and give a short continuation prompt. Keep a compact binding: delivery ID, absolute delivery directory, state path, current run, rules version, and fixed core/tool paths. After compaction, reload these references if absent; do not assume the full skill survived.
16
16
 
17
- Read [recovery and updates](references/recovery.md) on interruption, version mismatch or missing references; [sources and change flow](references/sources-and-changes.md) when adopting external material or revising reviewed work; [host access](references/hosts.md) only when access or host setup needs attention. Read [document patterns](references/document-patterns.md) only for an artifact being produced.
17
+ Read [recovery and updates](references/recovery.md) on interruption, version mismatch or missing references; [sources and change flow](references/sources-and-changes.md) when adopting external material or revising reviewed work; [host access](references/hosts.md) only when access or host setup needs attention. Read [document patterns](references/document-patterns.md) when establishing the artifact arrangement or producing a document. New deliveries keep formal documents at the root, source/request.md under source/, and structured state under .aimship/; status.md is the generated reading entry. On resume use the actual statePath returned by read, inspect view/document-change warnings, and explicitly repair or migrate as described in recovery. Do not treat a generated view as editable authority.
18
18
 
19
19
  The agent handles meaning, engineering and evidence. The local tools protect structure and writes; a successful command or a file's existence cannot establish goal completion. This skill does not schedule agents, open conversations, merge, deploy, or execute database changes by default.
@@ -0,0 +1,163 @@
1
+ {
2
+ "version": 1,
3
+ "documents": {
4
+ "prd": {
5
+ "path": "prd.md",
6
+ "sections": [
7
+ "info",
8
+ "goals",
9
+ "scope",
10
+ "requirements",
11
+ "states",
12
+ "acceptance",
13
+ "arrangement"
14
+ ]
15
+ },
16
+ "scope": {
17
+ "path": "scope.md",
18
+ "sections": [
19
+ "info",
20
+ "goals",
21
+ "adoption",
22
+ "exclusions",
23
+ "decisions",
24
+ "acceptance",
25
+ "arrangement"
26
+ ]
27
+ },
28
+ "tech-design": {
29
+ "path": "tech-design.md",
30
+ "sections": [
31
+ "basis",
32
+ "architecture",
33
+ "modules",
34
+ "data",
35
+ "errors",
36
+ "risks",
37
+ "coverage"
38
+ ]
39
+ },
40
+ "plan": {
41
+ "path": "plan.md",
42
+ "sections": [
43
+ "basis",
44
+ "batches",
45
+ "tasks",
46
+ "validation",
47
+ "execution",
48
+ "remaining"
49
+ ]
50
+ },
51
+ "testing": {
52
+ "path": "testing.md",
53
+ "sections": [
54
+ "basis",
55
+ "environment",
56
+ "cases",
57
+ "coverage",
58
+ "runs",
59
+ "conclusion"
60
+ ]
61
+ },
62
+ "review": {
63
+ "path": "review.md",
64
+ "sections": [
65
+ "basis",
66
+ "reviews",
67
+ "conclusion"
68
+ ]
69
+ },
70
+ "diagnosis": {
71
+ "path": "diagnosis.md",
72
+ "sections": [
73
+ "goals",
74
+ "symptoms",
75
+ "environment",
76
+ "evidence",
77
+ "causes",
78
+ "unknowns",
79
+ "conclusion"
80
+ ]
81
+ },
82
+ "handoff": {
83
+ "path": "handoff.md",
84
+ "sections": [
85
+ "goals",
86
+ "artifacts",
87
+ "usage",
88
+ "results",
89
+ "limits",
90
+ "outcomes"
91
+ ]
92
+ },
93
+ "change-log": {
94
+ "path": "change-log.md",
95
+ "sections": [
96
+ "changes",
97
+ "remaining"
98
+ ]
99
+ }
100
+ },
101
+ "stages": {
102
+ "frame": {
103
+ "kind": "requirement",
104
+ "section": "goals"
105
+ },
106
+ "requirements": {
107
+ "kind": "requirement",
108
+ "section": "requirements",
109
+ "sectionByKind": {
110
+ "prd": "requirements",
111
+ "scope": "adoption"
112
+ }
113
+ },
114
+ "scope": {
115
+ "kind": "requirement",
116
+ "section": "scope",
117
+ "sectionByKind": {
118
+ "prd": "scope",
119
+ "scope": "goals"
120
+ }
121
+ },
122
+ "prototype": {
123
+ "kind": "prototype",
124
+ "section": null
125
+ },
126
+ "diagnose": {
127
+ "kind": "diagnosis",
128
+ "section": "conclusion"
129
+ },
130
+ "tech-design": {
131
+ "kind": "tech-design",
132
+ "section": "coverage"
133
+ },
134
+ "contract": {
135
+ "kind": "contract",
136
+ "section": null
137
+ },
138
+ "plan": {
139
+ "kind": "plan",
140
+ "section": "tasks"
141
+ },
142
+ "test-design": {
143
+ "kind": "testing",
144
+ "section": "cases"
145
+ },
146
+ "implement": {
147
+ "kind": "plan",
148
+ "section": "execution"
149
+ },
150
+ "review": {
151
+ "kind": "review",
152
+ "section": "reviews"
153
+ },
154
+ "verify": {
155
+ "kind": "testing",
156
+ "section": "runs"
157
+ },
158
+ "handoff": {
159
+ "kind": "handoff",
160
+ "section": "outcomes"
161
+ }
162
+ }
163
+ }
@@ -4,7 +4,7 @@
4
4
 
5
5
  A delivery has one centralized material directory, a preserved original request, current requirements or adopted sources plus Scope, an endpoint, and a state record. Code stays in its existing locations. A single repository and several repositories use the same model; there is no project registration step, repository count limit, or route classification.
6
6
 
7
- Understand the intended result, supplied evidence, authorized changes and endpoint. Record known answers instead of asking again. Clarify only gaps that affect this work, with a recommendation and its consequence. Do not promote assumptions into user decisions. A design-only commission may have no repository. A short bug fix still needs its original report, context, expected behavior, scope and acceptance criteria, but not a full PRD.
7
+ Understand the intended result, supplied evidence, authorized changes and endpoint. Record known answers instead of asking again. Clarify only gaps that affect this work, with a recommendation and its consequence. Do not promote assumptions into user decisions. A design-only commission may have no repository. A short bug fix still needs its original report, context, expected behavior, scope and acceptance criteria, with a concise prd.md using the fixed outline.
8
8
 
9
9
  Use the configured material root (default `~/Aimship`), with one identifiable directory per delivery. Do not create it during installation. Before creating a new directory, use the user's explicit target or check a possible existing match; ambiguous candidates need selection, not a global “latest” pointer. If access fails, retain the request and intended location in the conversation and say what has not been saved. Continue the same delivery after access is restored.
10
10
 
@@ -16,7 +16,7 @@ Several independent deliveries may progress at the same time, each with its own
16
16
 
17
17
  Check when code scope is associated or changed, before entering implementation, or when an actual code working directory changes. Do not repeat it every turn or stage. Once the code scope is known:
18
18
 
19
- 1. Enumerate all delivery directories in the accessible configured material root and any other explicitly known/authorized material roots or delivery locations. Read their `state.json` summaries; do not limit the search to recent folders. Do not scan the whole disk, code trees, processes, PRDs or full histories. Do not discover unknown custom roots by searching the home directory. Report roots checked and inaccessible, invalid or unknown-format records; unseen custom roots and non-Aimship work remain outside the check.
19
+ 1. Enumerate all delivery directories in the accessible configured material root and any other explicitly known/authorized material roots or delivery locations. Use `scan <known-root>` or read both legacy `state.json` and new `.aimship/state.json` summaries; deduplicate deliveryId and report conflicting copies; do not limit the search to recent folders. Do not scan the whole disk, code trees, processes, PRDs or full histories. Do not discover unknown custom roots by searching the home directory. Report roots checked and inaccessible, invalid or unknown-format records; unseen custom roots and non-Aimship work remain outside the check.
20
20
  2. Exclude this delivery by `deliveryId`. Compare `repositories[].path` (the actual checkout/worktree root), role, `currentRun`, relevant `stageRuns` scope/progress, `outcome`, `next` and recorded update time. Repository IDs are local to each delivery and are not global repository identity. Use already recorded repository identity; when needed and accessible, read only local Git root/common-directory metadata for the associated paths to distinguish linked worktrees. Normalize existing paths to their actual location; nested paths in one checkout are not independent isolation. Distinct independent checkouts may share a recorded repository identity even with different Git directories. If identity/location cannot be established, report it as unknown rather than assuming separation.
21
21
  3. Include older active, blocked or uncertain deliveries. A `done` stage, completed handoff, old timestamp or declined acceptance alone does not close a delivery. A recorded met goal with no recorded remaining code work is ended; otherwise use the existing explicit closing conclusion if needed. `not-met` or `unknown` is not automatically ended. Read only the relevant Scope/plan/conclusion section when state summaries cannot establish writing intent or closure. Do not rewrite another delivery's state. Ended deliveries are not active writers, although their unmerged results or known dependencies can still matter.
22
22
  4. Compare actual writing intent and locations using the table below. A stage label alone is not permission to write code. Keep one brief result in the existing Scope/plan or results section, linked from the current delivery's existing references when needed: check occasion, roots/limits, relevant delivery IDs and recorded stages, actual directories, known dependencies and chosen arrangement. State stays authoritative; do not add a registry, index, second progress ledger or service.
@@ -33,7 +33,7 @@ Records can be stale and cannot detect all other work. Do not switch another ses
33
33
 
34
34
  ## Agree an ordered arrangement
35
35
 
36
- Choose stages from the responsibilities below based on the goal and existing inputs. Requirements and scope alignment are always needed; they may be one short conversation and one document. Adopt adequate existing artifacts as inputs without claiming their stages were newly executed. Do not create empty stage runs or templates.
36
+ Choose stages from the responsibilities below based on the goal and existing inputs. Requirements and scope alignment are always needed; use the fixed PRD/Scope conditions in [document patterns](document-patterns.md). Adopt adequate existing artifacts as inputs without claiming their stages were newly executed. Do not create empty stage runs or templates.
37
37
 
38
38
  Show the selected order and endpoint. If the user already specified or accepted this arrangement, record the decision and proceed. An unanswered proposal remains a proposal. Once agreed, follow the order; adding, dropping or reordering work needs a reason and an updated record. Important changes follow [change flow](sources-and-changes.md). A stage may recur as distinct instances with stable IDs and explicit scopes.
39
39
 
@@ -77,8 +77,12 @@ Use the installed tool's documented state protocol. Read the current revision be
77
77
 
78
78
  Important semantic changes retain historical review and test facts, mark affected conclusions for review and update the current editable authority. Do not overwrite external adopted sources. See [sources and change flow](sources-and-changes.md).
79
79
 
80
- At stable results save the state, remaining issues and next action. On a session switch give: “Use Aimship to continue <absolute delivery directory>; read state.json, the current goal/Scope and next-step inputs; <specific blocker if any>.” A new session reloads the same delivery, not the whole transcript. Use [recovery](recovery.md) for mismatch or uncertainty. Keep one active writing session per delivery.
80
+ At stable results save the state, remaining issues and next action. On a session switch give: “Use Aimship to continue <absolute delivery directory>; read the actual statePath returned by read (new: .aimship/state.json), the current goal/Scope and next-step inputs; <specific blocker if any>.” A new session reloads the same delivery, not the whole transcript. Use [recovery](recovery.md) for mismatch or uncertainty. Keep one active writing session per delivery.
81
81
 
82
82
  End when the current confirmed goal is met. For design work this means the agreed design, not code execution. For development it includes agreed review and verification. “Show me the result” does not shorten the goal. If the user explicitly changes the endpoint, preserve the old goal, decision and unfinished work, update the arrangement, and assess the revised goal. An incomplete handoff is allowed but is not goal completion.
83
83
 
84
84
  Small corrections within the original goal can reopen the same delivery: preserve its previous closure and result basis, increment the cycle, and review/verify affected work. A separate goal or substantial redesign starts a related delivery; retain the defect or source relationship without introducing parent/child scheduling. Prepared, accepted, merged and deployed are independent facts.
85
+
86
+ ## Formal artifacts and generated status
87
+
88
+ Read [document patterns](document-patterns.md) when establishing or revising the arrangement. All new formal documents use its fixed outlines and [artifact map](artifact-map.json). status.md is generated from structured state; tasks remain in plan.md. read/check/resume are read-only; inspect view and document-change warnings, and use explicit render to repair a stale/missing view. Preserve edits before rebuilding; never infer a business decision from hand-edited display text. After partial view failure use the saved revision, not a repeated business update.
@@ -1,19 +1,39 @@
1
- # Optional document patterns
1
+ # Delivery documents and traceability
2
2
 
3
- Use only the needed sections and artifacts. These are prompts for useful content, not empty files to generate automatically. Small work may combine diagnosis, test design, review, verification and handoff in `results.md`, with distinct referenced sections.
3
+ [artifact-map.json](artifact-map.json) is the shared mapping for stages, templates and checks. Use [templates](templates/prd.md) for newly authored documents; preserve section order and anchors, translate visible headings freely, and explain inapplicable sections. Templates are drafting guidance, not empty artifacts to generate for every delivery.
4
4
 
5
- **Requirements / Scope:** original request or adopted source; background; current goal and endpoint; included/excluded scope; authorized objects and constraints; requirements and acceptance criteria; source mapping; conflicts/decisions and open questions. Keep original input separate from revised current content.
5
+ One request has one directory. Formal documents are at its root. `source/request.md` preserves the original commission or source entry; other adopted versions stay in `source/` without overwriting. State, recovery copies, necessary evidence and generated host configuration belong under `.aimship/`. Generated `status.md` is the reading entry; no default INDEX, docs/sql hierarchy, AI copy, transcript or full export. A requested export is a dated object/version snapshot, never an editing authority.
6
6
 
7
- **Technical design:** current requirement basis; engineering constraints; chosen structure and alternatives/tradeoffs; interface links; data and compatibility; failure/recovery; risks and open decisions. For database changes link the independent SQL and its dialect/version, existing schema basis and execution conditions.
7
+ ## Input and output conditions
8
8
 
9
- **Plan:** batch ID and goal; requirement coverage; files/repositories and boundary; dependencies and interface; each batch review scope; overall review and independent verification. State tracks progress, so do not create a competing progress table.
9
+ Author current requirements in `prd.md`, including scope. Adopt an authoritative PRD/prototype through `scope.md`, preserving source/version/original IDs and gaps. Prior authorship does not make an adopted source editable. Only explicit maintenance of an editable authoritative PRD that completely expresses this commission's scope/authorization/endpoint permits registered reuse without another PRD/Scope. Scattered ideas/screenshots/reference cases are inputs to a PRD, not automatically authoritative requirements.
10
10
 
11
- **Verification plan:** requirement/risk → scenario and observable expected result; environment/data/tools; normal, error, boundary and relevant regression cases; known execution gaps. Keep missing-condition cases visible.
11
+ Interaction changes require a prototype when existing adopted material does not cover them and that work lies within the commissioned endpoint. New HTML uses `prototype/index.html`; register other agreed formats or reused entries. No interaction change means no invented prototype. Investigation/clarification-only requirements describe that commission and leave future development undefined.
12
12
 
13
- **Review record:** actor/source and date; exact objects/versions/differences; batch/overall/affected scope; requirements/design basis; findings, disposition and conclusion. A single-batch review explicitly covers both batch and overall responsibilities.
13
+ `plan` must finish before first implementation, even one small batch. `plan.md` holds both planned TASK/B records and actual execution, distinctly; state holds stage progress. `testing.md` holds cases/coverage and appended RUN records; verification follows overall review. Verify-only may register existing cases with version/coverage, without claiming test-design occurred. Review-only does not require implementation/tests. No arbitrary small-task merging, `implementation.md`, `test-plan.md`, `verification.md`, or `results.md` for new work.
14
14
 
15
- **Verification record:** actor/source and date; case version; tested code/build and environment; executed commands/operations; assertions and actual results; pass/fail/not-run/unknown; evidence locations; missing conditions and recovery action. Append a retest rather than rewriting the earlier failure.
15
+ Use `tech-design.md`. HTTP changes use `openapi.yaml` or the registered engineering authority; no extra contract wrapper. Non-HTTP formats are explicitly registered. Database changes require independent `database.sql` (one target) or `database-<id>.sql` per target before tech-design completes. SQL headers identify target/dialect/version/schema basis/conditions; ordered sections cite requirements/design. Never substitute a Markdown block or migration reference, or execute/change migration mechanisms without authorization.
16
16
 
17
- **Change:** stable ID; reason and decision source; old/new meaning; affected artifacts/runs; remaining uncertainty; work completed and review/verification result. One important decision affecting several artifacts remains one record.
17
+ `diagnosis.md` belongs only to an explicitly selected independent investigation. Known-cause bugs and ordinary development debugging do not add diagnose. Major unknowns require a recorded stage adjustment; hold dependent work. Implementation defects go to implementation, design errors to design, requirement gaps to PRD/Scope, incorrect expectations to versioned cases.
18
18
 
19
- **Handoff:** current goal (and explicit revision if any); completed deliverables and links; how to use/check; actual review and verification scope; limitations/remaining work; goal achievement, acceptance, merge and deployment as separate facts; continuation if incomplete.
19
+ ## Identities and references
20
+
21
+ New local identities: REQ (type distinguishes functional, shared rule, permission, technical or quality), AC, B, TASK, TC, REV, RUN, CHG, Q, PAGE. Start at 001, increment after the historical maximum (including retired), never reuse an identity for another obligation. Shared rules, independent acceptance and single-role access/performance requirements must not disappear inside generic feature prose. Preserve external prefixes and source/version namespace. Splits/merges receive new identities and map old to new; classification changes retain identity.
22
+
23
+ One effective definition per namespace/ID; historical versions are explicitly labeled. Keep ordinary repeated citations as references, not duplicate definitions. New local Markdown uses explicit anchors and definition metadata beside the actual table row/section:
24
+
25
+ ```markdown
26
+ <a id="req-001-v1"></a>
27
+ <!-- aimship:def {"id":"REQ-001","version":1,"status":"current","refs":["local:AC-001@1"]} -->
28
+ REQ-001 | functional | ... actual obligation and source ...
29
+ ```
30
+
31
+ Optional `namespace` defaults to `local`; source identity may use `source-v1`. External IDs keep their original spelling (including nonnumeric forms such as `acceptance.login`); `:` and `@` are reserved reference delimiters. For an external ID without a recognized prefix, set `kind` to REQ, AC, B, TASK, TC, REV, RUN, CHG, Q or PAGE when its role is needed for coverage checks, for example `{"namespace":"source-v1","id":"acceptance.login","kind":"AC","version":1,"status":"current","refs":[]}`. Reference it as `source-v1:acceptance.login@1`; do not renumber the source. `status` is current/historical/retired. `refs` uses namespace:ID@version (no implicit latest). Put metadata at the single definition; table/Markdown citations elsewhere may repeat. Retain old TC definitions or necessary prior meaning and increment versions for semantic changes. RUN metadata cites exact TC versions; its prose binds code/build, actual command/assertion/result/evidence. Tools check metadata resolution and basic coverage; Agent checks whether it truly covers every assertion.
32
+
33
+ REQ/source → AC → design/operation/SQL, TASK → actual repository object → REV, and AC → TC@version → RUN@object. Relationships live in those documents, not a trace.md or a second state matrix. Q is defined once in the discovering document with impact/needed decision/status/source; resolving it also updates the affected formal authority, linked to one CHG if meaning changes. Old review/test facts remain; only affected current applicability becomes needs-review.
34
+
35
+ ## Saving and checking
36
+
37
+ Register `documentPolicy` and `documents` per [CLI](../../../docs/cli.md). Draft at stage start; completion requires the mapped artifact and precise result anchor, not merely a filename. New Markdown anchors/metadata are machine-checkable; reused/remote/binary material needs recorded basis and explicit unchecked scope. Local references remain relative to delivery root even though JSON moved internally.
38
+
39
+ Save unfinished intent, update formal documents, inspect them, then revision-check state. A matching status revision does not prove documents unchanged. Read/check/resume diagnose only. A saved state with a failed view must be repaired using render at the returned revision, never replayed. Preserve hand-edited status before rebuilding and interpret intent against existing authority; it cannot confer acceptance or publishing permission.
@@ -5,11 +5,13 @@ Reuse the current session's access when normal file operations succeed. Do not c
5
5
  When access is missing, name the exact delivery/repository path and the needed operation. Retain the original request and continuation context. Do not disable host permission controls, grant the entire home directory or silently move materials into the repository.
6
6
 
7
7
  - Codex: explicitly select Aimship (CLI `$aimship`). If CLI startup is needed, verify the executable/version/help and use its supported `--cd` and repeated `--add-dir` syntax for the needed writable directories. For desktop, rely on the actual current interface or official guidance; never invent a directory button or claim a prepared path has been granted access.
8
- - Cursor: explicitly use `/aimship`. When useful, prepare `host/cursor.code-workspace` inside the delivery with `folders` entries only, referencing materials and repositories; preserve an existing workspace's settings. The user opens/adds folders when necessary. For CLI verify the exact executable (do not assume bare `agent` is Cursor) and current `--workspace` / `--add-dir` support.
8
+ - Cursor: explicitly use `/aimship`. When useful, prepare `.aimship/host/cursor.code-workspace` inside the delivery with absolute `folders[].path` entries only, referencing materials and repositories; preserve an existing workspace's settings. The user opens/adds folders when necessary. For CLI verify the exact executable (do not assume bare `agent` is Cursor) and current `--workspace` / `--add-dir` support.
9
9
  - Claude Code: explicitly use `/aimship`; verify current `/add-dir` or `--add-dir` behavior when additional access is needed. Do not fork the skill or start a subagent.
10
10
 
11
11
  Installation discovery, actual selection, model behavior, directory access and continuation are separate evidence levels. Codex/Cursor share the managed `.agents/skills/aimship` entry; Claude Code uses `.claude/skills/aimship`. A host may discover both, including when it is not registered with Aimship. Registration controls installer-managed connections, not another host’s compatibility scanning. Managed entries must point to the same stable installation; do not assume duplicate precedence or delete unrelated entries. Record duplicates as observed, and consult the published support matrix for tested surfaces.
12
12
 
13
13
  After compaction or manual switching, reload the fixed-version core, state and current-stage inputs if missing. Do not rely on a host retaining all skill text. If even the binding location is absent, request the delivery path. Re-invoking an entry to restore context continues the existing delivery; it does not create another one.
14
14
 
15
- When changing hosts, use the same delivery directory and state; do not duplicate requirements, Scope or design to establish a new session. Permissions must be granted separately in each host. Keep one writing host/session for the delivery. User-facing entry/reload and lifecycle instructions are in [the installation guide](../../../docs/installation.md).
15
+ When changing hosts, use the same delivery directory and state; do not duplicate requirements, Scope or design to establish a new session. Permissions must be granted separately in each host. Keep one writing host/session for the delivery. User-facing entry/reload and lifecycle instructions are in [the user guide](../../../docs/guide.md#hosts).
16
+
17
+ When reconciling an old Aimship-created workspace, resolve relative folders against its old file location before writing the new internal file. Verify actual checkout/worktree identity. Do not move or replace a user-owned existing workspace.
@@ -11,3 +11,11 @@ A tool/version mismatch stops new stage advancement. Save only in-progress facts
11
11
  Unknown newer formats remain readable as files but must not be overwritten or silently downgraded. Structural migrations require a supported conversion, necessary backup, clear description and validation. If no migration exists, preserve the materials and state the gap; do not invent historical stages. Compatible rules updates do not require new goal approval. Never sweep ended deliveries during install; only assess an ended delivery when it is reopened.
12
12
 
13
13
  Installation and data are separate. Explicit install manages product-owned files and host entries. Preserve user customizations and unrelated same-name skills; report the exact conflict. Uninstall retains delivery materials, customization and necessary configuration. Recovery retains a bounded previous installation, not every runtime forever. Consult the CLI help for implemented operations; do not bypass checks with raw state edits to obtain a success result.
14
+
15
+ ## Schema 1 to schema 2
16
+
17
+ Read reports the actual statePath and layout. Use migrate on one explicitly selected delivery, with the old revision and a compatibilityRef; then adopt active rules at the returned revision. The migration journal and byte-preserved source copies stay under .aimship. A pending journal requires the original migrate parameters, not a normal update. Conflicting dual layouts and unknown schemas never accept normal writes.
18
+
19
+ Migration converts state layout/schema only. Reconcile legacy requirements/design/implementation/test-plan/verification by their actual role, retaining original text and versioned results. Register legacy files as reused with a compatibility basis; create missing new plan/testing content for future work without inventing historical stages. Move necessary evidence and Aimship-created host files only with a concrete mapping, rewrite report links, and resolve workspace relative folders from the old location. Ordinary business source is never overwritten.
20
+
21
+ State is authoritative for stages; status is generated. JSON may have saved while view generation failed: use returned revision and render, never repeat update. Read/check/resume never repair implicitly. Inspect changed registered documents even when view revision matches. render --preserve-edits retains a modified status before replacing it; interpret intent and update the formal authority separately.