@awebai/oats 0.24.0 → 0.24.2
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 +224 -394
- package/bin/oats.mjs +192 -13
- package/capabilities/oats-aweb/bin/oats-aweb-binding.mjs +11 -0
- package/capabilities/oats-aweb/bin/oats-aweb.mjs +26 -0
- package/capabilities/oats-aweb/lib/binding-wire.mjs +214 -0
- package/capabilities/oats-aweb/lib/captured-execution.mjs +91 -0
- package/capabilities/oats-aweb/lib/captured-native.mjs +91 -0
- package/capabilities/oats-aweb/lib/invocation-shape.mjs +135 -0
- package/capabilities/oats-aweb/lib/portable-binding.mjs +146 -0
- package/capabilities/oats-aweb/lib/session-readiness.mjs +56 -0
- package/capabilities/oats-aweb/oats.json +12 -3
- package/capabilities/oats-okf/lib/captured-worker.mjs +12 -4
- package/capabilities/oats-okf/oats.json +1 -1
- package/docs/capabilities.md +4 -0
- package/docs/design/2026-09-20-redesign-program-board.md +83 -0
- package/docs/design/2026-09-20-workspace-and-portable-adoption-plan.md +287 -0
- package/docs/design/2026-09-20-workspace-onboarding-public.md +124 -0
- package/docs/design/README.md +42 -0
- package/docs/first-team.md +43 -1
- package/docs/knowledge-theory.md +353 -111
- package/docs/knowledge.md +10 -1
- package/docs/layers.md +89 -354
- package/docs/official-marketplace.md +84 -0
- package/docs/packages.md +15 -7
- package/docs/release-notes/v0.24.1.md +17 -0
- package/docs/release-notes/v0.24.2.md +21 -0
- package/docs/souls-and-instances.md +45 -7
- package/docs/workspace-adoption.md +314 -0
- package/docs/workspaces.md +154 -0
- package/injects/oats-portable.md +8 -5
- package/lib/core.mjs +89 -17
- package/lib/portable-onboarding.mjs +19 -0
- package/lib/prepared-resources.mjs +1 -1
- package/lib/provider-binding-broker.mjs +6 -1
- package/lib/setup-expert-source.mjs +76 -0
- package/package-catalog.json +9 -5
- package/package.json +3 -1
- package/skills/oats-config/SKILL.md +4 -5
- package/skills/oats-portable/SKILL.md +1 -2
- package/skills/oats-portable-artifacts/SKILL.md +2 -2
- package/souls/oats-setup-expert/AGENTS.md +60 -0
- package/souls/oats-setup-expert/soul.yaml +14 -0
- package/skills/oats-portable-setup/SKILL.md +0 -69
package/lib/core.mjs
CHANGED
|
@@ -51,6 +51,7 @@ import { buildCapturedInvocationContext, withCapturedInvocationContextFile } fro
|
|
|
51
51
|
import { readCapturedInstanceIndex, readCapturedInstanceMetadata, readCapturedInstanceAuthority, assertCapturedInstanceCustody,
|
|
52
52
|
recordCapturedCustodyFailure, markCapturedNativeScaffold, failCapturedNativeIntent, observeCapturedNativeTarget, setCapturedInstanceStatus, admitCapturedInstanceAction, beginCapturedIntent, settleCapturedIntent } from "./captured-instance-index.mjs";
|
|
53
53
|
import { validateExecutionBinding } from "./schedule-capsule.mjs";
|
|
54
|
+
import { parsePortableSource } from "./source-spec.mjs";
|
|
54
55
|
import { validateIntentRef } from "./captured-admission-shape.mjs";
|
|
55
56
|
export { readCapturedInstanceIndex, readCapturedInstanceMetadata, beginCapturedIntent, settleCapturedIntent };
|
|
56
57
|
export { buildCapturedInvocationContext, withCapturedInvocationContextFile };
|
|
@@ -61,6 +62,7 @@ import { validateCapturedLaunchRequest } from "./captured-launch-request.mjs";
|
|
|
61
62
|
import { validateCapabilityInputDeclarations } from "./capability-inputs.mjs";
|
|
62
63
|
import { validateCapturedSessionBackend, validateCapturedSessionTarget, assertCapturedSessionPlacement } from "./captured-session-backend.mjs";
|
|
63
64
|
import { createRepositoryTransaction } from "./repository-observation.mjs";
|
|
65
|
+
import { inspectPortableOnboarding as inspectOnboarding, describePortableOnboarding } from "./portable-onboarding.mjs";
|
|
64
66
|
import { readLock3 } from "./portable-lock.mjs";
|
|
65
67
|
import { portableScope, portableStateDirectory } from "./portable-state.mjs";
|
|
66
68
|
import { objectAt } from "./portable-shape.mjs";
|
|
@@ -111,6 +113,18 @@ export const OATS_VERSION = JSON.parse(readFileSync(join(PKG_ROOT, "package.json
|
|
|
111
113
|
/** Skills shipped with the kernel. Only oats-getting-started is ambient; spawn composes selected skills locally. */
|
|
112
114
|
export const PACKAGED_SKILLS_DIR = join(PKG_ROOT, "skills");
|
|
113
115
|
|
|
116
|
+
function soulRequirements(soulDir) {
|
|
117
|
+
const file = soulDir && join(soulDir, "soul.yaml");
|
|
118
|
+
return file && existsSync(file) ? withConfigFile(file, () => parseYamlNested(readFileSync(file, "utf8"))).requires : undefined;
|
|
119
|
+
}
|
|
120
|
+
function declaredOperationalCapabilities(soulDir) {
|
|
121
|
+
const capabilities = soulRequirements(soulDir)?.capabilities ?? {};
|
|
122
|
+
return ["oats.core", "oats.setup"].filter(id => Object.hasOwn(capabilities, id));
|
|
123
|
+
}
|
|
124
|
+
function legacyOperationalSkills(soulDir) {
|
|
125
|
+
return declaredOperationalCapabilities(soulDir).length ? [] : ["oats", "oats-config", "oats-packages"].map(name => ({ id: "kernel", path: join(PACKAGED_SKILLS_DIR, name) }));
|
|
126
|
+
}
|
|
127
|
+
|
|
114
128
|
// ---------- shell helpers ----------
|
|
115
129
|
function sh(cmdline) { return execSync(cmdline, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim(); }
|
|
116
130
|
function shTry(cmdline) { try { return sh(cmdline); } catch { return undefined; } }
|
|
@@ -1194,6 +1208,31 @@ export function approveAvailableCapability(deployment, artifactSet, capability,
|
|
|
1194
1208
|
return approveAvailableArtifact(deployment, artifactSet, capability, origin, loadRetainedManifest);
|
|
1195
1209
|
}
|
|
1196
1210
|
|
|
1211
|
+
/** Public read-only source/workspace inspection through the existing facade.
|
|
1212
|
+
* Owns only transient repository scratch, never deployment state, provisioning,
|
|
1213
|
+
* provider code, approvals or a serialized mutation witness. */
|
|
1214
|
+
export function inspectPortableOnboarding(input, { repositoryOptions = {} } = {}) {
|
|
1215
|
+
canonicalJson(input);
|
|
1216
|
+
const directory = mkdtempSync(join(realpathSync(tmpdir()), "oats-source-inspection-")), owned = lstatSync(directory);
|
|
1217
|
+
let repositories, primary;
|
|
1218
|
+
try {
|
|
1219
|
+
repositories = createRepositoryTransaction({ ...repositoryOptions, directory, accessContextKey: repositoryOptions.accessContextKey ?? "native" });
|
|
1220
|
+
return describePortableOnboarding(inspectOnboarding(input, { repositories }));
|
|
1221
|
+
} catch (error) { primary = error; throw error; }
|
|
1222
|
+
finally {
|
|
1223
|
+
try {
|
|
1224
|
+
repositories?.close();
|
|
1225
|
+
const current = lstatSync(directory);
|
|
1226
|
+
if (!current.isDirectory() || current.dev !== owned.dev || current.ino !== owned.ino) throw oatsError("source-incomplete", "inspection scratch ownership changed");
|
|
1227
|
+
removeOwnedStaging(directory);
|
|
1228
|
+
} catch (cleanup) {
|
|
1229
|
+
if (!primary) throw cleanup;
|
|
1230
|
+
const error = new AggregateError([primary, cleanup], "inspection and owned scratch cleanup failed", { cause: primary });
|
|
1231
|
+
error.code = primary.code; error.stagingPath = directory; throw error;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1197
1236
|
/** Explicit new-work preparation, with one frozen repository transaction.
|
|
1198
1237
|
* This first adapter captures command/curriculum profiles; provider-owned
|
|
1199
1238
|
* binding or distinct helper policy gaps return needs-configuration, never
|
|
@@ -4057,6 +4096,10 @@ export function composeInstanceAgentsMd(soulDir, contextDir, soulName, workMode,
|
|
|
4057
4096
|
if (!existsSync(agentsMd)) throw new Error(`canonical soul instructions missing: ${agentsMd}`);
|
|
4058
4097
|
const resolved = resolveOatsConfig(contextDir, soulName);
|
|
4059
4098
|
const wanted = [];
|
|
4099
|
+
const declaredOperations = declaredOperationalCapabilities(soulDir);
|
|
4100
|
+
const oatsCoreDeclared = declaredOperations.includes("oats.core");
|
|
4101
|
+
if (declaredOperations.length) resolved.kernelInjection = { inject: undefined,
|
|
4102
|
+
provenance: `declared ${declaredOperations.join(", ")} ${declaredOperations.length === 1 ? "capability" : "capabilities"}` };
|
|
4060
4103
|
const kernelInject = resolved.kernelInjection?.inject;
|
|
4061
4104
|
if (kernelInject && existsSync(kernelInject)) wanted.push(["kernel:oats", kernelInject]);
|
|
4062
4105
|
if (kind === "local") {
|
|
@@ -4078,7 +4121,7 @@ export function composeInstanceAgentsMd(soulDir, contextDir, soulName, workMode,
|
|
|
4078
4121
|
}
|
|
4079
4122
|
for (const inj of resolved.injects) wanted.push([`config:${inj.source}`, inj.file]);
|
|
4080
4123
|
const blocks = wanted.map(([source, file]) => ({ source, file, content: readFileSync(file, "utf8").trim() }));
|
|
4081
|
-
return { text: renderInstructionText(readFileSync(agentsMd, "utf8"), blocks), blocks, resolved };
|
|
4124
|
+
return { text: renderInstructionText(readFileSync(agentsMd, "utf8"), blocks), blocks, resolved, oatsCoreDeclared };
|
|
4082
4125
|
}
|
|
4083
4126
|
|
|
4084
4127
|
/** The skill entries a tree contributes — THE discovery rule, shared by preflight
|
|
@@ -4136,7 +4179,7 @@ export function planInstanceResources({ resolved, soulDir, agent, contextDir, co
|
|
|
4136
4179
|
}
|
|
4137
4180
|
};
|
|
4138
4181
|
|
|
4139
|
-
for (const path of
|
|
4182
|
+
for (const { path } of legacyOperationalSkills(soulDir)) {
|
|
4140
4183
|
add({ type: "skill-tree", source: "kernel", declared: basename(path), path: existsSync(path) ? path : undefined });
|
|
4141
4184
|
}
|
|
4142
4185
|
const soulSkills = soulDir && join(soulDir, "skills");
|
|
@@ -5084,15 +5127,31 @@ export function resolveYolo(value) {
|
|
|
5084
5127
|
throw new Error("yolo must be true or false");
|
|
5085
5128
|
}
|
|
5086
5129
|
|
|
5087
|
-
|
|
5130
|
+
/** Creation chooses a declared source, never acquisition/activation or a made-up
|
|
5131
|
+
* future release. Existing definitions (including deliberate removal) are kept. */
|
|
5132
|
+
function defaultOatsCoreRequirement() {
|
|
5133
|
+
const mapping = officialCapabilityPackage("oats.core");
|
|
5134
|
+
const entry = mapping.available && mapping.migratedCapability === "oats.core" && officialPackageCatalog()[mapping.package];
|
|
5135
|
+
if (entry && typeof entry.url === "string" && typeof entry.ref === "string" && entry.ref) {
|
|
5136
|
+
const parsed = parsePortableSource(`git:${entry.url}@${entry.ref}#${entry.path ?? DEFAULT_PACKAGE_PATH}`);
|
|
5137
|
+
return { requires: { capabilities: { "oats.core": { source: `${parsed.source}#${parsed.path}` } } }, notes: [] };
|
|
5138
|
+
}
|
|
5139
|
+
return { requires: undefined, notes: [{ code: "needs-configuration", message: "oats.core has no published revision in the official package catalog; no source was invented. Add its explicit requirement after the catalog entry is available, or use --no-oats-core to opt out at creation." }] };
|
|
5140
|
+
}
|
|
5141
|
+
|
|
5142
|
+
export function writeSoul(root, { name, kind, repo, work, runtime, model, yolo, description, type, instructions, oatsCore = true }) {
|
|
5088
5143
|
yolo = resolveYolo(yolo);
|
|
5144
|
+
if (typeof oatsCore !== "boolean") throw oatsError("E_BAD_ARGS", "oatsCore must be a boolean");
|
|
5089
5145
|
const agentDir = agentDirOf(root, name, kind);
|
|
5090
|
-
const soulDir = soulOf(agentDir);
|
|
5146
|
+
const soulDir = soulOf(agentDir), soulFile = join(soulDir, "soul.yaml");
|
|
5147
|
+
const { requires, notes } = existsSync(soulFile)
|
|
5148
|
+
? { requires: soulRequirements(soulDir), notes: [] }
|
|
5149
|
+
: oatsCore ? defaultOatsCoreRequirement() : { requires: undefined, notes: [] };
|
|
5091
5150
|
mkdirSync(soulDir, { recursive: true });
|
|
5092
5151
|
mkdirSync(join(agentDir, "instances"), { recursive: true });
|
|
5093
|
-
writeFileSync(
|
|
5152
|
+
writeFileSync(soulFile, yamlFlat({
|
|
5094
5153
|
name, kind, description, type, repo, work: work || "checkout", runtime: runtime || "pi", model, yolo,
|
|
5095
|
-
}));
|
|
5154
|
+
}) + (requires === undefined ? "" : `requires: ${JSON.stringify(requires)}\n`));
|
|
5096
5155
|
const agentsMd = join(soulDir, "AGENTS.md");
|
|
5097
5156
|
if (instructions !== undefined || !existsSync(agentsMd)) {
|
|
5098
5157
|
writeFileSync(agentsMd, instructions ?? defaultSoulAgentsMd(name, description));
|
|
@@ -5106,7 +5165,7 @@ export function writeSoul(root, { name, kind, repo, work, runtime, model, yolo,
|
|
|
5106
5165
|
home: soulDir, instance: name, agentName: name, soulDir,
|
|
5107
5166
|
contextDir: ctx, workspaceDir: workspaceOf(root), rootDir: root, resolved,
|
|
5108
5167
|
});
|
|
5109
|
-
return { agentDir, soulDir };
|
|
5168
|
+
return { agentDir, soulDir, ...(notes.length ? { notes } : {}) };
|
|
5110
5169
|
}
|
|
5111
5170
|
function defaultSoulAgentsMd(name, description) {
|
|
5112
5171
|
return `# ${name}
|
|
@@ -5128,8 +5187,8 @@ export function createAgent(root, o) {
|
|
|
5128
5187
|
// kind: "local" → a FULL soul (memory, skills, instances) under the scope's
|
|
5129
5188
|
// local-agents/ — uncommitted by contract; otherwise a committed persistent soul.
|
|
5130
5189
|
const kind = o.local || o.kind === "local" ? "local" : "persistent";
|
|
5131
|
-
const { agentDir } = writeSoul(root, { ...o, name, kind });
|
|
5132
|
-
return { agent: name, kind, soul: soulOf(agentDir) };
|
|
5190
|
+
const { agentDir, notes } = writeSoul(root, { ...o, name, kind });
|
|
5191
|
+
return { agent: name, kind, soul: soulOf(agentDir), ...(notes ? { notes } : {}) };
|
|
5133
5192
|
}
|
|
5134
5193
|
|
|
5135
5194
|
/** Upsert a local agent soul (from raw instructions or a Claude-style def file).
|
|
@@ -5156,13 +5215,13 @@ export function upsertLocalAgent(root, o) {
|
|
|
5156
5215
|
const existing = findAgent(root, name);
|
|
5157
5216
|
if (existing && existing.kind !== "local") throw new Error(`"${name}" is a persistent agent — spawn it instead`);
|
|
5158
5217
|
if (!existing && instructions === undefined) throw new Error(`local agent "${name}" needs instructions (none on disk yet)`);
|
|
5159
|
-
writeSoul(root, {
|
|
5160
|
-
name, kind: "local",
|
|
5218
|
+
const { notes } = writeSoul(root, {
|
|
5219
|
+
name, kind: "local", oatsCore: o.oatsCore,
|
|
5161
5220
|
repo: repo ?? existing?.repo, work: work ?? existing?.work,
|
|
5162
5221
|
runtime: runtime ?? existing?.runtime, model: model ?? existing?.model, yolo: yolo ?? existing?.yolo,
|
|
5163
5222
|
description: description ?? existing?.description, instructions,
|
|
5164
5223
|
});
|
|
5165
|
-
return findAgent(root, name);
|
|
5224
|
+
return { ...findAgent(root, name), ...(notes ? { notes } : {}) };
|
|
5166
5225
|
}
|
|
5167
5226
|
/** Back-compat alias: older installed capabilities (oats-okf ≤1.3.x) call this. */
|
|
5168
5227
|
export const upsertTmpAgent = upsertLocalAgent;
|
|
@@ -6110,7 +6169,7 @@ export function spawnInstance(root, agent, o = {}) {
|
|
|
6110
6169
|
symlinkSync("AGENTS.md", join(home, "CLAUDE.md"));
|
|
6111
6170
|
|
|
6112
6171
|
// Runtime-neutral exact skill materialization. No harness receives ambient workspace/package skills.
|
|
6113
|
-
const sources =
|
|
6172
|
+
const sources = legacyOperationalSkills(soulDir);
|
|
6114
6173
|
const soulSkills = join(soulDir, "skills");
|
|
6115
6174
|
if (existsSync(soulSkills)) sources.push({ id: "soul", path: soulSkills });
|
|
6116
6175
|
for (const cap of resolvedCfg.capabilities) for (const path of cap.skills || []) sources.push({ id: cap.id, path });
|
|
@@ -7121,9 +7180,11 @@ function runtimeAuthorityOf(baseline) {
|
|
|
7121
7180
|
return { launched: true, tmux: { session: tmux.session, window: tmux.window, socket: resolve(tmux.socket) } };
|
|
7122
7181
|
}
|
|
7123
7182
|
|
|
7124
|
-
/** Session control uses the
|
|
7183
|
+
/** Session control uses the independent endpoint receipt and, for captured
|
|
7184
|
+
* homes, its original incarnation's existing home/work custody. */
|
|
7125
7185
|
function instanceSessionTarget(home) {
|
|
7126
7186
|
if (typeof home !== "string" || !isAbsolute(home)) throw oatsError("E_BAD_ARGS", "session needs an absolute instance home");
|
|
7187
|
+
const requestedHome = home;
|
|
7127
7188
|
home = realPathOrNearest(home);
|
|
7128
7189
|
let baseline, meta;
|
|
7129
7190
|
try {
|
|
@@ -7132,18 +7193,29 @@ function instanceSessionTarget(home) {
|
|
|
7132
7193
|
} catch (e) { throw oatsError("E_RUNTIME_ENDPOINT_UNKNOWN", `cannot read session receipt for ${home}: ${e.message}`); }
|
|
7133
7194
|
const authority = baseline.version === RETIRE_BASELINE_VERSION && baseline.home === home && runtimeAuthorityOf(baseline);
|
|
7134
7195
|
if (!authority) throw oatsError("E_RUNTIME_ENDPOINT_UNKNOWN", `independent session receipt is missing or invalid for ${home}`);
|
|
7196
|
+
let io;
|
|
7197
|
+
if ([baseline, meta].some(value => ["executionBinding", "incarnationId", "captured"].some(key => Object.hasOwn(value, key)))) {
|
|
7198
|
+
// The independent receipt remains the anchor even if mutable home markers
|
|
7199
|
+
// are removed. Missing/invalid captured evidence never falls back to legacy.
|
|
7200
|
+
validateExecutionBinding(baseline.executionBinding);
|
|
7201
|
+
const { row } = readCapturedInstanceAuthority(baseline.executionBinding.deployment, requestedHome);
|
|
7202
|
+
if (row.incarnationId !== baseline.incarnationId || canonicalJson(row.executionBinding) !== canonicalJson(baseline.executionBinding)) {
|
|
7203
|
+
throw oatsError("E_RUNTIME_AUTHORITY_MISMATCH", "captured incarnation disagrees with independent session receipt");
|
|
7204
|
+
}
|
|
7205
|
+
io = { exec: (...args) => { assertCapturedInstanceCustody(row); return execFileSync(...args); } };
|
|
7206
|
+
}
|
|
7135
7207
|
const endpointAgrees = authority.sessionTarget
|
|
7136
7208
|
? !meta.tmux && ["backend", "binary", "socket", "workspaceId", "paneId", "terminalId", "protocol"].every((key) => meta.sessionTarget?.[key] === authority.sessionTarget[key])
|
|
7137
7209
|
: !meta.sessionTarget && meta.tmux?.session === authority.tmux?.session && meta.tmux?.window === authority.tmux?.window && resolve(meta.tmux?.socket || ".") === authority.tmux?.socket;
|
|
7138
7210
|
if (meta.launched !== authority.launched || (authority.launched && !endpointAgrees)) throw oatsError("E_RUNTIME_AUTHORITY_MISMATCH", "instance metadata disagrees with independent session receipt");
|
|
7139
|
-
return { home, target: authority.launched ? authority.sessionTarget || { backend: "tmux", ...authority.tmux } : undefined };
|
|
7211
|
+
return { home, target: authority.launched ? authority.sessionTarget || { backend: "tmux", ...authority.tmux } : undefined, io };
|
|
7140
7212
|
}
|
|
7141
7213
|
|
|
7142
7214
|
export function inspectInstanceSession(home) {
|
|
7143
7215
|
if (typeof home === "string" && isAbsolute(home) && !existsSync(home)) return { home: realPathOrNearest(home), backend: null, present: false, state: "stopped" };
|
|
7144
7216
|
const s = instanceSessionTarget(home);
|
|
7145
7217
|
if (!s.target) return { home: s.home, backend: null, present: false, state: "not-launched" };
|
|
7146
|
-
try { return { home: s.home, ...inspectSessionTarget(s.target) }; }
|
|
7218
|
+
try { return { home: s.home, ...inspectSessionTarget(s.target, s.io) }; }
|
|
7147
7219
|
catch (e) { throw oatsError("E_SESSION_UNAVAILABLE", `cannot inspect session: ${e.message}`); }
|
|
7148
7220
|
}
|
|
7149
7221
|
|
|
@@ -7157,7 +7229,7 @@ export function inputInstanceSession(home, text) {
|
|
|
7157
7229
|
if (typeof text !== "string" || !text.trim() || text.includes("\0") || Buffer.byteLength(text) > 256 * 1024) throw oatsError("E_BAD_ARGS", "session input must be nonempty text without NUL, at most 256 KiB");
|
|
7158
7230
|
const s = instanceSessionTarget(home);
|
|
7159
7231
|
if (!s.target) throw oatsError("E_SESSION_NOT_RUNNING", "instance was not launched");
|
|
7160
|
-
try { return { home: s.home, ...inputSessionTarget(s.target, text) }; }
|
|
7232
|
+
try { return { home: s.home, ...inputSessionTarget(s.target, text, s.io) }; }
|
|
7161
7233
|
catch (e) { throw oatsError("E_SESSION_INPUT_FAILED", `cannot submit session input: ${e.message}`); }
|
|
7162
7234
|
}
|
|
7163
7235
|
|
|
@@ -189,6 +189,25 @@ export function inspectPortableOnboarding(input, { repositories } = {}) {
|
|
|
189
189
|
return result;
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
/** Metadata-only public view, NOT a serialized fresh-inspection witness or a
|
|
193
|
+
* preparation request. Provider payloads/adoption values have not gone through
|
|
194
|
+
* their owner's nonsecret classification and must not leak through inspection. */
|
|
195
|
+
export function describePortableOnboarding(inspection) {
|
|
196
|
+
if (!issued.has(inspection)) throw oatsError("invalid-declaration", "inspection summary requires an issued onboarding view");
|
|
197
|
+
const reference = value => ({ source: value.source, soul: value.soul, revision: value.revision, alias: value.alias,
|
|
198
|
+
adoptionPresent: Object.hasOwn(value, "adoption") });
|
|
199
|
+
const { reference: selected, exports, ...source } = inspection.source;
|
|
200
|
+
return freezeJson({ ...inspection,
|
|
201
|
+
source: { ...source, adoptionPresent: Object.hasOwn(selected, "adoption"), exports: {
|
|
202
|
+
souls: (exports.souls ?? []).map(({ path, definition, description }) => ({ path, definition, ...(description === undefined ? {} : { description }) })),
|
|
203
|
+
packages: (exports.packages ?? []).map(({ path, description }) => ({ path, ...(description === undefined ? {} : { description }) })),
|
|
204
|
+
knowledge: (exports.knowledge ?? []).map(({ contract, version }) => ({ contract, version, payloadOmitted: true })),
|
|
205
|
+
} },
|
|
206
|
+
workspace: inspection.workspace ? { ...inspection.workspace, imports: inspection.workspace.imports.map(reference) } : null,
|
|
207
|
+
omitted: { providerPayloads: true, adoptionValues: true },
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
192
211
|
/** Ephemeral local root custody, not a new persistent authority/schema. Ordinary
|
|
193
212
|
* project content changes are allowed; replacing the inspected directories or
|
|
194
213
|
* provisioning a previously absent deployment requires a fresh inspection. */
|
|
@@ -23,7 +23,7 @@ export function completePreparedResources({ seed, plan, manifests, mode, deploym
|
|
|
23
23
|
}
|
|
24
24
|
if (problems.length) return { record: null, problems };
|
|
25
25
|
const snapshot = join(directory, "kernel-resources"); mkdirSync(snapshot); mkdirSync(join(snapshot, "injects"));
|
|
26
|
-
const kernelSkills = ["oats-portable", "oats-portable-
|
|
26
|
+
const kernelSkills = ["oats-portable", "oats-portable-artifacts"];
|
|
27
27
|
const workInjection = (workMode) => workMode === "directory" ? "portable-work-directory.md" : `work-${workMode}.md`;
|
|
28
28
|
const injectionFiles = ["oats-portable.md", "portable-instance-boundary.md", ...kernel.workModes.map(workInjection)];
|
|
29
29
|
// Only known kernel resources, never a sweep of the checkout/node_modules,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/** Execute only an approved retained provider command. Preparation does not need
|
|
2
2
|
* a fabricated complete resolution merely to obtain its binding. */
|
|
3
3
|
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { realpathSync } from 'node:fs';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
4
6
|
import { canonicalJson } from './portable-values.mjs';
|
|
5
7
|
import { objectAt } from './portable-shape.mjs';
|
|
6
8
|
import { validateArtifactSet } from './resolution-shape.mjs';
|
|
@@ -12,6 +14,9 @@ import { validateBindingInterface } from './provider-binding.mjs';
|
|
|
12
14
|
import { BINDING_LIMITS, validateBindingRequest, decodeBindingResponse } from './provider-binding-wire.mjs';
|
|
13
15
|
import { oatsError } from './errors.mjs';
|
|
14
16
|
|
|
17
|
+
// Same kernel-owned CLI locator as lifecycle hooks; never caller env or PATH.
|
|
18
|
+
const CLI_BIN=fileURLToPath(new URL('../bin/oats.mjs',import.meta.url));
|
|
19
|
+
|
|
15
20
|
export function invokeProviderBinding(options,codecs) {
|
|
16
21
|
canonicalJson(options);
|
|
17
22
|
objectAt(options,['deployment','artifacts','capability','phase','settings','input','timeoutMs'],['deployment','artifacts','capability','phase','settings','input']);
|
|
@@ -46,7 +51,7 @@ export function invokeProviderBinding(options,codecs) {
|
|
|
46
51
|
const [script,...args]=spec.trim().split(/\s+/),file=codecs.executable(manifest,script);
|
|
47
52
|
if (!file) throw oatsError('resource-not-found','provider codec executable is unavailable');
|
|
48
53
|
const environment=Object.fromEntries(Object.entries(process.env).filter(([key])=>! /^(OATS_|OAS_|PI_)/.test(key)));
|
|
49
|
-
Object.assign(environment,{OATS_CAPABILITY:capability,OATS_CAPABILITY_ROOT:root,OATS_SETTINGS:canonicalJson(settings,BINDING_LIMITS)});
|
|
54
|
+
Object.assign(environment,{OATS_CAPABILITY:capability,OATS_CAPABILITY_ROOT:root,OATS_SETTINGS:canonicalJson(settings,BINDING_LIMITS),OATS_CLI_BIN:realpathSync(CLI_BIN)});
|
|
50
55
|
let result;
|
|
51
56
|
try {
|
|
52
57
|
result=spawnSync(process.execPath,[file,...args],{cwd:root,env:environment,input:canonicalJson(request,BINDING_LIMITS),
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** Read the setup edition as data for a CLASSIC local bootstrap copy.
|
|
2
|
+
* No provider code, workspace activation, enrollment or captured identity. */
|
|
3
|
+
import { lstatSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, realpathSync, rmSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { parsePortableSoul } from './portable-soul.mjs';
|
|
8
|
+
import { parseRepositorySource, parsePortableSource } from './source-spec.mjs';
|
|
9
|
+
import { createRepositoryTransaction } from './repository-observation.mjs';
|
|
10
|
+
import { createWorkspaceDiscovery } from './workspace-discovery.mjs';
|
|
11
|
+
import { packageIntegrity } from './core.mjs';
|
|
12
|
+
import { oatsError } from './errors.mjs';
|
|
13
|
+
|
|
14
|
+
export const SETUP_EXPERT = 'oats-setup-expert';
|
|
15
|
+
export const SETUP_CAPABILITIES = Object.freeze(['oats.core', 'oats.setup']);
|
|
16
|
+
const EXPORT = `souls/${SETUP_EXPERT}`;
|
|
17
|
+
function validateEdition(bytes) {
|
|
18
|
+
const { declaration: soul } = parsePortableSoul(bytes);
|
|
19
|
+
const required = soul.requires || {}, caps = required.capabilities || {};
|
|
20
|
+
if (soul.name !== SETUP_EXPERT || soul.work !== 'directory'
|
|
21
|
+
|| Object.keys(required).some(key => key !== 'capabilities')
|
|
22
|
+
|| Object.keys(caps).length !== 2 || SETUP_CAPABILITIES.some(id => caps[id]?.source !== 'repo:oats-package' || Object.keys(caps[id]).some(key => key !== 'source'))
|
|
23
|
+
|| ['knowledge', 'messaging', 'tasks'].some(slot => soul.defaults?.[slot] !== 'none')
|
|
24
|
+
|| Object.keys(soul.defaults || {}).some(key => !['knowledge', 'messaging', 'tasks'].includes(key))
|
|
25
|
+
|| soul.knowledge || soul.teams?.length || soul.resources?.length || soul.yolo === true || soul.backend || soul['launch-config']) {
|
|
26
|
+
throw oatsError('needs-configuration', 'classic setup bootstrap needs the provider-independent directory edition with only oats.core/oats.setup; use explicit portable preparation for other requirements');
|
|
27
|
+
}
|
|
28
|
+
return soul;
|
|
29
|
+
}
|
|
30
|
+
function repositoryRequest(source) {
|
|
31
|
+
if (typeof source !== 'string' || source.includes('#')) throw oatsError('invalid-source', '--workspace needs a Git repository source, optionally @revision, without a package fragment');
|
|
32
|
+
try { return { source: parseRepositorySource(source).normalized }; }
|
|
33
|
+
catch {
|
|
34
|
+
const parsed = parsePortableSource(source);
|
|
35
|
+
if (parsed.kind !== 'git') throw oatsError('invalid-source', '--workspace needs an explicit Git repository source');
|
|
36
|
+
return { source: `git:${parsed.url}`, revision: parsed.selector };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function loadSetupExpertEdition(workspace, repositoryOptions = {}) {
|
|
41
|
+
if (workspace === undefined) {
|
|
42
|
+
const root = fileURLToPath(new URL(`../${EXPORT}/`, import.meta.url));
|
|
43
|
+
return { declaration: validateEdition(readFileSync(join(root, 'soul.yaml'))), instructions: readFileSync(join(root, 'AGENTS.md'), 'utf8'),
|
|
44
|
+
source: { kind: 'packaged-definition', captured: false }, packageIntegrity: null };
|
|
45
|
+
}
|
|
46
|
+
const request = repositoryRequest(workspace), scratch = realpathSync(mkdtempSync(join(tmpdir(), 'oats-setup-source-'))), owned = lstatSync(scratch);
|
|
47
|
+
let transaction;
|
|
48
|
+
try {
|
|
49
|
+
transaction = createRepositoryTransaction({ ...repositoryOptions, directory: scratch, accessContextKey: 'explicit-setup-source' });
|
|
50
|
+
const discovery = createWorkspaceDiscovery(transaction), origin = { kind: 'operator', document: { kind: 'operator', id: 'oats-onboard' }, pointer: '/workspace' };
|
|
51
|
+
const observed = transaction.observe(request.source, { ...request, origin });
|
|
52
|
+
const workspaceDoc = transaction.readFile(observed, 'oats-workspace.yaml', { optional: true });
|
|
53
|
+
const view = workspaceDoc ? discovery.readWorkspace({ ...request, origin }) : null;
|
|
54
|
+
const reference = view?.parsed.imports.find(item => item.alias === SETUP_EXPERT)
|
|
55
|
+
?? { source: request.source, revision: observed.source.commit, soul: EXPORT, alias: SETUP_EXPERT };
|
|
56
|
+
// A selected workspace import never falls back if its exact source fails.
|
|
57
|
+
const imported = discovery.importSoul(reference, { origin });
|
|
58
|
+
if (reference.adoption) throw oatsError('needs-configuration', 'classic setup copies an edition only; provider adoption values require the portable preparation path');
|
|
59
|
+
const declaration = validateEdition(transaction.readFile(imported.observation, imported.definition).bytes);
|
|
60
|
+
const projection = join(scratch, 'edition');
|
|
61
|
+
transaction.materialize(imported.observation, imported.roots, projection);
|
|
62
|
+
const soulRoot = join(projection, dirname(imported.definition)), body = join(soulRoot, 'AGENTS.md'), alias = join(soulRoot, 'CLAUDE.md');
|
|
63
|
+
if (!lstatSync(body).isFile() || !lstatSync(alias).isSymbolicLink() || readlinkSync(alias) !== 'AGENTS.md'
|
|
64
|
+
|| readdirSync(soulRoot).some(name => !['soul.yaml', 'AGENTS.md', 'CLAUDE.md'].includes(name))) {
|
|
65
|
+
throw oatsError('source-incomplete', 'classic setup edition must have canonical AGENTS.md/CLAUDE.md and no omitted private skill or knowledge trees');
|
|
66
|
+
}
|
|
67
|
+
return { declaration, instructions: readFileSync(body, 'utf8'), packageIntegrity: packageIntegrity(join(projection, 'oats-package')),
|
|
68
|
+
source: { kind: 'exported-edition-copy', source: imported.reference.source, revision: imported.observation.source.commit,
|
|
69
|
+
path: imported.reference.soul, workspaceRevision: view?.source.commit ?? null, captured: false } };
|
|
70
|
+
} finally {
|
|
71
|
+
transaction?.close();
|
|
72
|
+
const current = lstatSync(scratch);
|
|
73
|
+
if (!current.isDirectory() || current.dev !== owned.dev || current.ino !== owned.ino) throw oatsError('source-unavailable', 'setup source scratch ownership changed');
|
|
74
|
+
rmSync(scratch, { recursive: true });
|
|
75
|
+
}
|
|
76
|
+
}
|
package/package-catalog.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
+
"policy": "docs/official-marketplace.md",
|
|
2
3
|
"packages": {
|
|
3
4
|
"oats.okf": {
|
|
4
5
|
"url": "https://github.com/awebai/oats-okf.git",
|
|
5
|
-
"ref": "v2.1.
|
|
6
|
+
"ref": "v2.1.1",
|
|
6
7
|
"path": "oats-package"
|
|
7
8
|
},
|
|
8
9
|
"oats.aweb": {
|
|
9
10
|
"url": "https://github.com/awebai/oats-aweb.git",
|
|
10
|
-
"ref": "v1.
|
|
11
|
+
"ref": "v1.11.0",
|
|
11
12
|
"path": "oats-package"
|
|
12
13
|
},
|
|
13
14
|
"oats.jira": {
|
|
@@ -30,9 +31,9 @@
|
|
|
30
31
|
"ref": "v1.0.0",
|
|
31
32
|
"path": "oats-package"
|
|
32
33
|
},
|
|
33
|
-
"oats.
|
|
34
|
+
"oats.framework": {
|
|
34
35
|
"url": "https://github.com/awebai/oats.git",
|
|
35
|
-
"ref": "
|
|
36
|
+
"ref": "oats-framework/v1.1.1",
|
|
36
37
|
"path": "oats-package"
|
|
37
38
|
}
|
|
38
39
|
},
|
|
@@ -65,6 +66,9 @@
|
|
|
65
66
|
"oas.review": {
|
|
66
67
|
"package": "oats.dev",
|
|
67
68
|
"capability": "oats.review"
|
|
68
|
-
}
|
|
69
|
+
},
|
|
70
|
+
"oats.knowledge-theory": "oats.framework",
|
|
71
|
+
"oats.core": "oats.framework",
|
|
72
|
+
"oats.setup": "oats.framework"
|
|
69
73
|
}
|
|
70
74
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awebai/oats",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.2",
|
|
4
4
|
"description": "OATS (Open Agent Team Specification) — durable souls, disposable instances, targetable capability packages, and the runtime-neutral oats CLI/kernel.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
"docs/",
|
|
37
37
|
"README.md",
|
|
38
38
|
"package-catalog.json",
|
|
39
|
+
"souls/oats-setup-expert/soul.yaml",
|
|
40
|
+
"souls/oats-setup-expert/AGENTS.md",
|
|
39
41
|
"packages/record/bin/",
|
|
40
42
|
"packages/record/lib/",
|
|
41
43
|
"packages/record/docs/",
|
|
@@ -4,17 +4,16 @@ description: >-
|
|
|
4
4
|
Use only for configuring a legacy uncaptured OATS deployment with the current
|
|
5
5
|
oats-config.yaml cascade, including legacy activation, agent types, targeting,
|
|
6
6
|
overrides, and templates. Triggers: "legacy oats-config", "uncaptured config",
|
|
7
|
-
"oats use", or "agent type".
|
|
8
|
-
|
|
7
|
+
"oats use", or "agent type". This legacy policy is not a portable preparation
|
|
8
|
+
authority and never fills a captured record.
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Configuring legacy uncaptured OATS
|
|
12
12
|
|
|
13
13
|
> **Legacy-only procedure.** The cascading scopes, agent-type targeting and
|
|
14
14
|
> closest-team rules below are compatibility behavior for uncaptured instances.
|
|
15
|
-
> They are not portable composition authorities.
|
|
16
|
-
>
|
|
17
|
-
> fallback during captured preparation or dispatch.
|
|
15
|
+
> They are not portable composition authorities. Never consult this cascade as
|
|
16
|
+
> a fallback during captured preparation or dispatch.
|
|
18
17
|
|
|
19
18
|
Config lives in `oats-config.yaml` at laptop (`~`), workspace, and repository
|
|
20
19
|
levels; resolution walks from a soul's repository outward, closest scope wins.
|
|
@@ -112,5 +112,4 @@ still refuse. Do not strip selectors or call legacy forms as a workaround. A sca
|
|
|
112
112
|
may contain external hook effects; preserve it and escalate rather than deleting
|
|
113
113
|
it. A scaffold marked `spawned-launch-pending` is not a running instance.
|
|
114
114
|
|
|
115
|
-
Use **oats-portable-
|
|
116
|
-
**oats-portable-artifacts** for exact approval and retained A/B diagnostics.
|
|
115
|
+
Use **oats-portable-artifacts** for exact approval and retained A/B diagnostics.
|
|
@@ -59,5 +59,5 @@ or source checkout.
|
|
|
59
59
|
- Partial or unknown historical evidence is inspectable but never executable.
|
|
60
60
|
- No unattended approval, background refresh, or automatic advancement exists.
|
|
61
61
|
|
|
62
|
-
Use
|
|
63
|
-
|
|
62
|
+
Use `oats prepare --help` for new preparation inputs and **oats-portable** to
|
|
63
|
+
invoke the resulting exact record.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# OATS Setup Expert
|
|
2
|
+
|
|
3
|
+
Help an operator turn an empty deployment into a deliberately configured OATS
|
|
4
|
+
workspace. Explain the next small decision, inspect the existing state, obtain
|
|
5
|
+
approval for effects, and verify the result before moving on. Do not replace
|
|
6
|
+
working deployments or turn setup into an implicit enrollment operation.
|
|
7
|
+
|
|
8
|
+
## Your supplied procedures
|
|
9
|
+
|
|
10
|
+
- Load **oats-workspace-setup** for workspace/source discovery and adoption:
|
|
11
|
+
declare, inspect, prepare, approve, scaffold and start are different steps.
|
|
12
|
+
- Load **oats-config** for version-scoped classic configuration and targeting;
|
|
13
|
+
never use its cascade to fill a missing captured input.
|
|
14
|
+
- Load **oats-packages** for official package discovery, acquisition, exact locks,
|
|
15
|
+
executable approval and updates.
|
|
16
|
+
- Load **oats-operate** for lifecycle, directory boundaries and supported CLI
|
|
17
|
+
operations; load **oats-souls** for source editions, roster and relations.
|
|
18
|
+
|
|
19
|
+
Use the procedures actually included in your composition. Do not fetch a current
|
|
20
|
+
skill or invent a command when an older installed version lacks a feature.
|
|
21
|
+
|
|
22
|
+
## Setup sequence
|
|
23
|
+
|
|
24
|
+
1. Establish the operator's intended deployment, work target and workspace/source
|
|
25
|
+
separately. Inspect existing configuration, locks and souls before proposing
|
|
26
|
+
changes. A workspace is a shared definition, not a shared live runtime.
|
|
27
|
+
2. Explain `oats-workspace.yaml` and each member's separate `oats.yaml` exports
|
|
28
|
+
and backlink. Check reciprocal observations; discovery is neither membership
|
|
29
|
+
enrollment nor capability activation. Pin imports only after a source is
|
|
30
|
+
published at a real reviewed revision; never invent a future commit or tag.
|
|
31
|
+
3. Select capabilities and their exact sources with the operator. New souls
|
|
32
|
+
declare removable `oats.core` explicitly. Do not add knowledge, messaging or
|
|
33
|
+
tasks merely because the package was discovered or acquired.
|
|
34
|
+
4. Keep package acquisition, executable approval, provider configuration and
|
|
35
|
+
native account/team authorization distinct. Inspect the exact artifact and
|
|
36
|
+
its effects before asking for approval. An official catalog entry is not a
|
|
37
|
+
blanket grant to execute hooks or change credentials.
|
|
38
|
+
5. Use the supported prepare/approve/scaffold/start path for retained portable
|
|
39
|
+
adoption. Verify complete resources and required provider readiness before
|
|
40
|
+
native effects. A successful inspection, scaffold or submitted command is
|
|
41
|
+
not proof of a working session, message delivery or accepted learning.
|
|
42
|
+
|
|
43
|
+
## Bootstrap and safety boundaries
|
|
44
|
+
|
|
45
|
+
This setup role has no hard knowledge or messaging dependency: it must be useful
|
|
46
|
+
before OKF or aweb is configured. Its defaults permit none. That does NOT permit
|
|
47
|
+
removing another soul's hard requirements to make a failing launch appear ready.
|
|
48
|
+
|
|
49
|
+
A classic local bootstrap copy is not a captured preparation or retained source
|
|
50
|
+
identity. Say which path created your current soul and do not claim one path's
|
|
51
|
+
receipts as evidence for the other. Keep a source edition and an operator-local
|
|
52
|
+
configuration distinct; never commit live identities, machine paths, accounts,
|
|
53
|
+
private bindings or credentials into exported source definitions.
|
|
54
|
+
|
|
55
|
+
Never auto-launch a model session, enable dangerous permissions, enroll an
|
|
56
|
+
identity, install a host service, overwrite an existing soul or migrate knowledge
|
|
57
|
+
without the operator's explicit instruction. Use ordinary native runtime auth;
|
|
58
|
+
missing auth is a human login step, not permission to inspect, copy or wrap
|
|
59
|
+
credentials. Preserve existing instances, pending jobs, locks and failed receipts.
|
|
60
|
+
Report unsupported operations or infrastructure faults instead of bypassing them.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
schemaVersion: 1
|
|
2
|
+
name: oats-setup-expert
|
|
3
|
+
description: Guide an operator through OATS workspace adoption and explicit capability setup.
|
|
4
|
+
work: directory
|
|
5
|
+
requires:
|
|
6
|
+
capabilities:
|
|
7
|
+
oats.core:
|
|
8
|
+
source: repo:oats-package
|
|
9
|
+
oats.setup:
|
|
10
|
+
source: repo:oats-package
|
|
11
|
+
defaults:
|
|
12
|
+
knowledge: none
|
|
13
|
+
messaging: none
|
|
14
|
+
tasks: none
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: oats-portable-setup
|
|
3
|
-
description: >-
|
|
4
|
-
Use when preparing a source-complete portable soul for a fresh deployment,
|
|
5
|
-
selecting a workspace-advertised import, or reasoning about source,
|
|
6
|
-
deployment, work target, team, adoption, and provider-binding inputs. Triggers:
|
|
7
|
-
"oats prepare", "portable setup", "import soul by reference", "fresh OATS
|
|
8
|
-
deployment". Do not use legacy oats-config.yaml cascading as portable policy.
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# Preparing portable souls
|
|
12
|
-
|
|
13
|
-
Portable preparation has two policy authorities and one resolver:
|
|
14
|
-
|
|
15
|
-
1. The soul supplies intrinsic hard requirements and rebindable defaults.
|
|
16
|
-
2. The workspace supplies admission, workspace defaults, imports/adoption,
|
|
17
|
-
stores, team references, and catalogs.
|
|
18
|
-
3. Explicit operator choices may rebind defaults and bindings but cannot erase
|
|
19
|
-
hard requirements.
|
|
20
|
-
|
|
21
|
-
There is no repository-default tier and no agent-type precedence in this model.
|
|
22
|
-
A package source, soul source, deployment/install location, work target, and team
|
|
23
|
-
membership are separate facts. Never derive one from another.
|
|
24
|
-
|
|
25
|
-
## Implemented preparation
|
|
26
|
-
|
|
27
|
-
Prepare an explicit source export by reference:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
oats prepare \
|
|
31
|
-
--dir <absolute-deployment> \
|
|
32
|
-
--source <git-repository> --revision <selector> \
|
|
33
|
-
--export <exported-soul-path> --alias <local-alias> \
|
|
34
|
-
[--work directory] --json
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Prepare an alias advertised by an explicit workspace:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
oats prepare \
|
|
41
|
-
--dir <absolute-deployment> \
|
|
42
|
-
--workspace <git-repository> [--workspace-revision <selector>] \
|
|
43
|
-
--alias <advertised-alias> [--work directory] --json
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Preparation resolves one source observation, retains the required source and
|
|
47
|
-
software trees, resolves provider-owned nonsecret bindings through the same
|
|
48
|
-
choice engine, prepares dedicated compatible helpers, and publishes a complete
|
|
49
|
-
record before returning a resolution. It does not launch, enroll identities,
|
|
50
|
-
create teams, approve executables, or infer a publisher workspace.
|
|
51
|
-
|
|
52
|
-
## Read the result correctly
|
|
53
|
-
|
|
54
|
-
- `resolution: null` means no executable captured record was published.
|
|
55
|
-
- `needs-configuration` identifies missing bindings/provider inputs; do not fill
|
|
56
|
-
them from current config.
|
|
57
|
-
- `approval-required` can accompany a complete retained record; approval remains
|
|
58
|
-
a separate explicit action.
|
|
59
|
-
- `responsibleHuman: null` means messaging is explicitly disabled.
|
|
60
|
-
- Helper-authored software, knowledge, team, or resource policy currently
|
|
61
|
-
requires dedicated preparation and refuses instead of inheriting the parent.
|
|
62
|
-
|
|
63
|
-
## Not yet a public input
|
|
64
|
-
|
|
65
|
-
An explicit standalone context key, onboarding inspection facade, non-directory
|
|
66
|
-
work-target placement/setup, and managed launch/runtime capture are still being
|
|
67
|
-
integrated. Do not invent flags or derive a standalone key from a path, username,
|
|
68
|
-
source alias, or machine identity. A workspace context and standalone context are
|
|
69
|
-
mutually exclusive.
|