atris 3.27.0 → 3.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -3
- package/README.md +5 -5
- package/atris/features/company-brain-sync/build.md +4 -4
- package/atris/features/company-brain-sync/idea.md +2 -2
- package/atris/features/company-brain-sync/validate.md +19 -19
- package/atris/skills/aeo/SKILL.md +1 -1
- package/atris/skills/autopilot/SKILL.md +2 -2
- package/atris/skills/slides/SKILL.md +2 -2
- package/bin/atris.js +23 -18
- package/commands/aeo.js +1 -1
- package/commands/autopilot.js +1 -1
- package/commands/computer.js +9 -9
- package/commands/feedback.js +1 -1
- package/commands/init.js +2 -2
- package/commands/live.js +3 -3
- package/commands/plugin.js +1 -1
- package/commands/pull.js +4 -4
- package/commands/security-review.js +360 -0
- package/commands/task.js +0 -1
- package/lib/security-scan.js +440 -0
- package/package.json +1 -8
- package/atris/wiki/concepts/agent-activation-contract.md +0 -81
- package/atris/wiki/concepts/workspace-initialization-contract.md +0 -73
- package/atris/wiki/index.md +0 -31
- package/atris/wiki/sources/atris-labs-2026-05-10.txt +0 -14
- package/atris/wiki/sources/atris-labs-goals-2026-05-10.txt +0 -14
- package/atris/wiki/sources/atrisos-generative-ui-product-surface-2026-05-10.txt +0 -10
- package/atris/wiki/sources/jack-dorsey-2026-05-10.txt +0 -12
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// atris security scan — deterministic secrets / PII / privacy detectors (no LLM).
|
|
4
|
+
//
|
|
5
|
+
// A finding is a fact (file:line + rule + severity), so it drops straight into a
|
|
6
|
+
// loop / mission / CI gate and doubles as a SOC 2 evidence artifact (machine
|
|
7
|
+
// JSON). Precision over recall: a noisy gate gets muted, and a muted gate is dead.
|
|
8
|
+
// Suppress a single line with a trailing `atris-allow-secret` comment.
|
|
9
|
+
//
|
|
10
|
+
// Zero external deps (Node built-ins only) — repo contract.
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const crypto = require('crypto');
|
|
15
|
+
const { execFileSync } = require('child_process');
|
|
16
|
+
|
|
17
|
+
const SCAN_EXTS = new Set([
|
|
18
|
+
'.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx', '.json', '.md', '.mdx', '.txt',
|
|
19
|
+
'.yml', '.yaml', '.env', '.sh', '.bash', '.zsh', '.py', '.rb', '.go', '.java',
|
|
20
|
+
'.php', '.toml', '.ini', '.cfg', '.conf', '.html', '.vue', '.svelte', '.patch',
|
|
21
|
+
]);
|
|
22
|
+
const SKIP_DIRS = new Set([
|
|
23
|
+
'node_modules', '.git', 'dist', 'build', '.next', 'coverage', 'out', 'vendor',
|
|
24
|
+
'.cache', '__pycache__', '_archive', 'fixtures', 'fixture', 'snapshots',
|
|
25
|
+
'snapshot', '__snapshots__', 'testdata',
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const DEFAULT_BASELINE = '.security-review.baseline.json';
|
|
29
|
+
|
|
30
|
+
// Lines that opt out, and placeholder noise we never flag.
|
|
31
|
+
const ALLOW_MARKER = /atris-allow-secret|atris-security-ignore|#\s*nosec/i;
|
|
32
|
+
const PLACEHOLDER = /\b(?:example|placeholder|your[_-]?|my[_-]?key|xxx+|redacted|dummy|sample|changeme|test[_-]?key|fake|fixture|benchmark|mock|not[-_ ]?real|should[-_ ]?not[-_ ]?leak|should[-_ ]?redact|no\s+matches|process\.env|getenv|os\.environ|import\.meta\.env)\b|<[a-z0-9_-]+>/i;
|
|
33
|
+
const PLACEHOLDER_VALUE_FRAGMENTS = [
|
|
34
|
+
'should-not-leak', 'should_redact', 'fixture-token', 'xoxb-secret',
|
|
35
|
+
'xoxb-should-not-leak', 'xoxb-fixture-token', 'your_api_key',
|
|
36
|
+
'your-api-key', 'your_key_here', 'your-token', 'secret_yyy', 'secret-yyy',
|
|
37
|
+
'paid-audit-api-key', 'prod-api-key', 'free-plan-key', 'test1234567890',
|
|
38
|
+
'abc123def456', 'abcdefghijklmnopqrstuvwxyz', 'abcdef1234567890',
|
|
39
|
+
'sk-test', 'sk-abc', 'dev-secret', 'test-secret', 'my-super-secret',
|
|
40
|
+
'same-secret', 'correct_secret', 'oauth_access_token', 'some_secret',
|
|
41
|
+
'private-key-pem', 'shared-secret', 'oauth-access', 'ext-oauth',
|
|
42
|
+
'environment-secret', 'immutable-secret', 'member-secret', 'atris_sk_test',
|
|
43
|
+
];
|
|
44
|
+
const COMMON_PLACEHOLDER_VALUES = new Set([
|
|
45
|
+
'password', 'passw0rd', 'hunter2', 'hunter2hunter2', 'secret', 'secret123',
|
|
46
|
+
'test', 'testpass123', 'test_token_123', 'dev-user', 'dev-secret-do-not-use-in-prod',
|
|
47
|
+
'sovereign-local', 'my-hmac-key', 'env-token', 'audit-key',
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
// High-precision secret patterns. Known-provider keys become critical only when
|
|
51
|
+
// their value quality check says the variable part looks real.
|
|
52
|
+
const SECRET_RULES = [
|
|
53
|
+
{ id: 'private-key', sev: 'critical', cat: 'secret', re: /(-----BEGIN (?:RSA |EC |OPENSSH |PGP |DSA |ENCRYPTED )?PRIVATE KEY-----)/, known: true, noEntropy: true, why: 'private key committed in source' },
|
|
54
|
+
{ id: 'aws-access-key-id', sev: 'critical', cat: 'secret', re: /\b((?:AKIA|ASIA)[0-9A-Z]{16})\b/, known: true, why: 'real-looking AWS access key id' },
|
|
55
|
+
{ id: 'github-token', sev: 'critical', cat: 'secret', re: /\b(gh[pousr]_[A-Za-z0-9]{36,}|github_pat_[A-Za-z0-9_]{40,})\b/, known: true, why: 'real-looking GitHub token' },
|
|
56
|
+
{ id: 'slack-token', sev: 'critical', cat: 'secret', re: /\b(xox[baprs]-[A-Za-z0-9-]{10,})\b/, known: true, why: 'real-looking Slack token' },
|
|
57
|
+
{ id: 'slack-webhook', sev: 'critical', cat: 'secret', re: /(https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9/_-]{20,})/, known: true, why: 'real-looking Slack incoming webhook url' },
|
|
58
|
+
{ id: 'openai-key', sev: 'critical', cat: 'secret', re: /\b(sk-proj-[A-Za-z0-9_-]{32,}|sk-[A-Za-z0-9]{32,})\b/, known: true, why: 'real-looking OpenAI-style API key' },
|
|
59
|
+
{ id: 'anthropic-key', sev: 'critical', cat: 'secret', re: /\b(sk-ant-[A-Za-z0-9_-]{20,})\b/, known: true, why: 'real-looking Anthropic API key' },
|
|
60
|
+
{ id: 'google-api-key', sev: 'critical', cat: 'secret', re: /\b(AIza[0-9A-Za-z_-]{35})\b/, known: true, why: 'real-looking Google API key' },
|
|
61
|
+
{ id: 'stripe-key', sev: 'critical', cat: 'secret', re: /\b([rs]k_live_[A-Za-z0-9]{20,})\b/, known: true, why: 'real-looking Stripe live key' },
|
|
62
|
+
{ id: 'npm-token', sev: 'critical', cat: 'secret', re: /\b(npm_[A-Za-z0-9]{36})\b/, known: true, why: 'real-looking npm access token' },
|
|
63
|
+
{ id: 'sendgrid-key', sev: 'critical', cat: 'secret', re: /\b(SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43})\b/, known: true, why: 'real-looking SendGrid API key' },
|
|
64
|
+
{ id: 'jwt', sev: 'medium', cat: 'secret', re: /\b(eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{6,})\b/, why: 'JWT-like token in source' },
|
|
65
|
+
{ id: 'bearer-token', sev: 'medium', cat: 'secret', re: /\bBearer\s+([A-Za-z0-9._~+/=-]{24,})/, why: 'hardcoded Bearer token' },
|
|
66
|
+
{ id: 'assigned-secret', sev: 'high', cat: 'secret', re: /\b(?:password|passwd|pwd|secret|api[_-]?key|apikey|apiKey|access[_-]?token|accessToken|auth[_-]?token|authToken|client[_-]?secret|clientSecret|private[_-]?key|privateKey)\b\s*[:=]\s*(['"`])([^'"`\s]{8,})\1/i, valueGroup: 2, why: 'real-looking hardcoded credential value' },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// Personal data. Home-path is the recurring leak (a username + local layout).
|
|
70
|
+
const PII_RULES = [
|
|
71
|
+
{ id: 'home-path', sev: 'medium', cat: 'pii', re: /(?:\/Users\/|\/home\/)(?!runner\/|runner\b|root\/|root\b|ubuntu\/|ubuntu\b|user\/|user\b|node\/)[a-z][a-z0-9_.-]+/i, why: 'personal home path leaks a username + local layout (use os.homedir()/relative)' },
|
|
72
|
+
{ id: 'windows-home-path', sev: 'medium', cat: 'pii', re: /[A-Za-z]:\\Users\\(?!Public\b)[^\\\s'"]+/, why: 'personal Windows path leaks a username' },
|
|
73
|
+
{ id: 'email', sev: 'low', cat: 'pii', re: /\b[A-Za-z0-9._%+-]+@(?!example\.|sentry\.io|test\b|localhost|[\w.-]*\.local\b|sub\.)[A-Za-z0-9.-]+\.(?:com|net|org|ai|io|dev|co)\b/, why: 'email address (possible PII)' },
|
|
74
|
+
{ id: 'phone', sev: 'low', cat: 'pii', re: /(?<![\d.\w])(?:\+?1[-.\s])?\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}(?![\d.\w])/, why: 'phone-number-shaped string' },
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
// Code-execution risks. For an AI CLI that runs autonomous loops and shells out,
|
|
78
|
+
// these are the "are we actually safe" checks beyond data exposure. eval/Function
|
|
79
|
+
// are almost never legitimate (HIGH); shelling out with interpolated input is the
|
|
80
|
+
// command-injection class (MEDIUM — common and sometimes safe, so it asks for a
|
|
81
|
+
// human look rather than hard-failing the gate).
|
|
82
|
+
const CODE_RULES = [
|
|
83
|
+
{ id: 'eval-call', sev: 'medium', cat: 'code', re: /(?<![.\w])eval\((?!\s*\))/, why: 'eval() can run untrusted code' },
|
|
84
|
+
{ id: 'new-function', sev: 'medium', cat: 'code', re: /\bnew\s+Function\(/, why: 'new Function() can run untrusted code' },
|
|
85
|
+
{ id: 'shell-exec-interpolation', sev: 'medium', cat: 'code', re: /\b(?:exec|execSync)\s*\(\s*[`'"][^`'"]*(?:\$\{|"\s*\+|'\s*\+)/, why: 'shell exec with interpolated input (command-injection risk; prefer execFile/spawn with an args array)' },
|
|
86
|
+
{ id: 'child-process-shell-true', sev: 'medium', cat: 'code', re: /\bshell\s*:\s*true\b/, why: 'child_process shell:true enables shell interpretation of args' },
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
// extra per-line excludes that keep email/path rules from firing on safe noise
|
|
90
|
+
const EMAIL_SAFE = /\b(?:noreply|no-reply|support|hello|info|contact|team|hi|admin|press)@/i;
|
|
91
|
+
|
|
92
|
+
const RULES = [...SECRET_RULES, ...PII_RULES, ...CODE_RULES];
|
|
93
|
+
const SEVERITIES = ['critical', 'high', 'medium', 'low'];
|
|
94
|
+
const SEVERITY_RANK = { low: 0, medium: 1, high: 2, critical: 3 };
|
|
95
|
+
|
|
96
|
+
// Filenames that should never be committed (checked against the tracked file list).
|
|
97
|
+
const SENSITIVE_FILE_RE = /(?:^|\/)(?:\.env(?:\.[\w.-]+)?|id_rsa|id_dsa|id_ed25519|.*\.pem|.*\.pfx|.*\.p12|.*\.keystore|credentials\.json|\.npmrc|\.pypirc|\.netrc|secrets?\.(?:json|ya?ml|env))$/i;
|
|
98
|
+
const SENSITIVE_FILE_ALLOW = /\.env\.example$|\.env\.sample$|\.env\.template$/i;
|
|
99
|
+
|
|
100
|
+
function normalizeSnippet(snippet) {
|
|
101
|
+
return String(snippet || '').replace(/\s+/g, ' ').trim();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function sha256(value) {
|
|
105
|
+
return crypto.createHash('sha256').update(String(value)).digest('hex');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function shannonEntropy(value) {
|
|
109
|
+
const s = String(value || '');
|
|
110
|
+
if (!s) return 0;
|
|
111
|
+
const freq = new Map();
|
|
112
|
+
for (const ch of s) freq.set(ch, (freq.get(ch) || 0) + 1);
|
|
113
|
+
let entropy = 0;
|
|
114
|
+
for (const count of freq.values()) {
|
|
115
|
+
const p = count / s.length;
|
|
116
|
+
entropy -= p * Math.log2(p);
|
|
117
|
+
}
|
|
118
|
+
return entropy;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function charClassCount(value) {
|
|
122
|
+
const s = String(value || '');
|
|
123
|
+
let count = 0;
|
|
124
|
+
if (/[a-z]/.test(s)) count++;
|
|
125
|
+
if (/[A-Z]/.test(s)) count++;
|
|
126
|
+
if (/[0-9]/.test(s)) count++;
|
|
127
|
+
if (/[^A-Za-z0-9]/.test(s)) count++;
|
|
128
|
+
return count;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function uniqueChars(value) {
|
|
132
|
+
return new Set(String(value || '').split('')).size;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function hasRepeatedChunk(value) {
|
|
136
|
+
const s = String(value || '');
|
|
137
|
+
if (/^(.)\1{7,}$/.test(s)) return true;
|
|
138
|
+
for (let size = 2; size <= Math.min(24, Math.floor(s.length / 2)); size++) {
|
|
139
|
+
if (s.length % size !== 0) continue;
|
|
140
|
+
const chunk = s.slice(0, size);
|
|
141
|
+
if (chunk.repeat(s.length / size) === s) return true;
|
|
142
|
+
}
|
|
143
|
+
return /(.{8,})\1/.test(s);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function hasSequentialRun(value) {
|
|
147
|
+
const s = String(value || '').toLowerCase();
|
|
148
|
+
const sequences = [
|
|
149
|
+
'abcdefghijklmnopqrstuvwxyz',
|
|
150
|
+
'zyxwvutsrqponmlkjihgfedcba',
|
|
151
|
+
'0123456789',
|
|
152
|
+
'9876543210',
|
|
153
|
+
'qwertyuiopasdfghjklzxcvbnm',
|
|
154
|
+
];
|
|
155
|
+
for (const seq of sequences) {
|
|
156
|
+
for (let i = 0; i <= seq.length - 8; i++) {
|
|
157
|
+
if (s.includes(seq.slice(i, i + 8))) return true;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function stripKnownPrefix(value) {
|
|
164
|
+
return String(value || '')
|
|
165
|
+
.replace(/^(?:AKIA|ASIA)/, '')
|
|
166
|
+
.replace(/^github_pat_/, '')
|
|
167
|
+
.replace(/^gh[pousr]_/, '')
|
|
168
|
+
.replace(/^xox[baprs]-/, '')
|
|
169
|
+
.replace(/^https:\/\/hooks\.slack\.com\/services\//, '')
|
|
170
|
+
.replace(/^sk-proj-/, '')
|
|
171
|
+
.replace(/^sk-ant-/, '')
|
|
172
|
+
.replace(/^sk-/, '')
|
|
173
|
+
.replace(/^AIza/, '')
|
|
174
|
+
.replace(/^[rs]k_live_/, '')
|
|
175
|
+
.replace(/^npm_/, '')
|
|
176
|
+
.replace(/^SG\./, '');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function isPlaceholderSecretValue(value, line = '') {
|
|
180
|
+
const raw = String(value || '');
|
|
181
|
+
const lowValue = raw.toLowerCase();
|
|
182
|
+
const lowLine = String(line || '').toLowerCase();
|
|
183
|
+
if (!raw) return true;
|
|
184
|
+
if (PLACEHOLDER.test(lowLine) || PLACEHOLDER.test(lowValue)) return true;
|
|
185
|
+
if (COMMON_PLACEHOLDER_VALUES.has(lowValue)) return true;
|
|
186
|
+
if (/^<[^>]+>$/.test(raw) || /^\$\{?[A-Z0-9_]+\}?$/.test(raw)) return true;
|
|
187
|
+
if (/^(?:x+|\*+|\.+|-+|_+)$/.test(raw)) return true;
|
|
188
|
+
if (PLACEHOLDER_VALUE_FRAGMENTS.some((fragment) => lowValue.includes(fragment) || lowLine.includes(fragment))) return true;
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function secretQuality(value, { known = false, noEntropy = false } = {}, line = '') {
|
|
193
|
+
const raw = String(value || '').trim();
|
|
194
|
+
if (isPlaceholderSecretValue(raw, line)) return { ok: false, reason: 'placeholder' };
|
|
195
|
+
if (noEntropy) return { ok: true, entropy: null, reason: 'private-key-marker' };
|
|
196
|
+
|
|
197
|
+
const core = stripKnownPrefix(raw).replace(/[-_.:/]/g, '');
|
|
198
|
+
const entropy = shannonEntropy(core);
|
|
199
|
+
const minLength = known ? 16 : 18;
|
|
200
|
+
const minEntropy = known ? 3.0 : 3.35;
|
|
201
|
+
const minUnique = known ? 8 : 10;
|
|
202
|
+
|
|
203
|
+
if (core.length < minLength) return { ok: false, entropy, reason: 'too-short' };
|
|
204
|
+
if (uniqueChars(core) < minUnique) return { ok: false, entropy, reason: 'low-variety' };
|
|
205
|
+
if (charClassCount(core) < 2) return { ok: false, entropy, reason: 'single-charset' };
|
|
206
|
+
if (entropy < minEntropy) return { ok: false, entropy, reason: 'low-entropy' };
|
|
207
|
+
if (hasRepeatedChunk(core)) return { ok: false, entropy, reason: 'repeating' };
|
|
208
|
+
if (hasSequentialRun(core)) return { ok: false, entropy, reason: 'sequential' };
|
|
209
|
+
return { ok: true, entropy, reason: 'real-shaped' };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function extractSecretValue(rule, match) {
|
|
213
|
+
if (!match) return '';
|
|
214
|
+
if (rule.valueGroup != null) return match[rule.valueGroup] || '';
|
|
215
|
+
return match[1] || match[0] || '';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function secretSnippet(rule, value) {
|
|
219
|
+
const safeValue = String(value || '');
|
|
220
|
+
if (rule.id === 'private-key') return 'private key header';
|
|
221
|
+
const prefix = safeValue.slice(0, Math.min(12, safeValue.length));
|
|
222
|
+
return `${prefix}${safeValue.length > prefix.length ? '...' : ''} sha256:${sha256(safeValue).slice(0, 12)}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function findingFingerprint(finding) {
|
|
226
|
+
const raw = [
|
|
227
|
+
finding.rule,
|
|
228
|
+
finding.cat,
|
|
229
|
+
finding.file || '',
|
|
230
|
+
normalizeSnippet(finding.snippet),
|
|
231
|
+
].join('|');
|
|
232
|
+
return sha256(raw);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function withFingerprint(finding) {
|
|
236
|
+
return { ...finding, fingerprint: findingFingerprint(finding), suppressed: false };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function scanLine(line, rules = RULES) {
|
|
240
|
+
if (ALLOW_MARKER.test(line)) return [];
|
|
241
|
+
const findings = [];
|
|
242
|
+
for (const rule of rules) {
|
|
243
|
+
const m = rule.re.exec(line);
|
|
244
|
+
if (!m) continue;
|
|
245
|
+
if (rule.cat === 'secret') {
|
|
246
|
+
const value = extractSecretValue(rule, m);
|
|
247
|
+
const quality = secretQuality(value, rule, line);
|
|
248
|
+
if (!quality.ok) continue;
|
|
249
|
+
findings.push({
|
|
250
|
+
rule: rule.id,
|
|
251
|
+
sev: rule.sev,
|
|
252
|
+
cat: rule.cat,
|
|
253
|
+
why: rule.why,
|
|
254
|
+
snippet: secretSnippet(rule, value),
|
|
255
|
+
entropy: quality.entropy == null ? null : Number(quality.entropy.toFixed(2)),
|
|
256
|
+
});
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (rule.id === 'email' && EMAIL_SAFE.test(line)) continue;
|
|
260
|
+
findings.push({ rule: rule.id, sev: rule.sev, cat: rule.cat, why: rule.why, snippet: m[0].trim().slice(0, 60) });
|
|
261
|
+
}
|
|
262
|
+
return findings;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function scanText(text, rules = RULES) {
|
|
266
|
+
const out = [];
|
|
267
|
+
const lines = String(text || '').split('\n');
|
|
268
|
+
for (let i = 0; i < lines.length; i++) {
|
|
269
|
+
for (const f of scanLine(lines[i], rules)) out.push({ ...f, line: i + 1 });
|
|
270
|
+
}
|
|
271
|
+
return out;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Code-execution rules only make sense in actual code files — `eval(` written in
|
|
275
|
+
// a markdown doc is prose, not a vuln.
|
|
276
|
+
const CODE_EXTS = new Set(['.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx', '.py', '.rb', '.go', '.java', '.php', '.sh', '.bash', '.zsh']);
|
|
277
|
+
const DOC_EXTS = new Set(['.md', '.mdx', '.txt', '.patch']);
|
|
278
|
+
|
|
279
|
+
function rulesForFile(file) {
|
|
280
|
+
return CODE_EXTS.has(path.extname(file)) ? RULES : RULES.filter((r) => r.cat !== 'code');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function scanFile(file, rules) {
|
|
284
|
+
let text;
|
|
285
|
+
try { text = fs.readFileSync(file, 'utf8'); } catch { return []; }
|
|
286
|
+
const ext = path.extname(file);
|
|
287
|
+
return scanText(text, rules || rulesForFile(file)).map((f) => {
|
|
288
|
+
if (DOC_EXTS.has(ext) && f.rule === 'assigned-secret' && f.sev === 'high') {
|
|
289
|
+
return { ...f, sev: 'medium', why: 'credential-looking value in documentation; verify before publishing', file };
|
|
290
|
+
}
|
|
291
|
+
return { ...f, file };
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Repo-level hygiene: a sensitive file being tracked at all is a finding,
|
|
296
|
+
// independent of its contents.
|
|
297
|
+
function sensitiveFileFindings(relPaths) {
|
|
298
|
+
const out = [];
|
|
299
|
+
for (const p of relPaths) {
|
|
300
|
+
if (shouldSkipRelPath(p)) continue;
|
|
301
|
+
if (SENSITIVE_FILE_RE.test(p) && !SENSITIVE_FILE_ALLOW.test(p)) {
|
|
302
|
+
out.push({ file: p, line: 0, rule: 'tracked-sensitive-file', sev: 'high', cat: 'privacy', why: 'sensitive file is tracked in git (gitignore + remove it)', snippet: path.basename(p) });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return out;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function gitTrackedFiles(root, { staged = false } = {}) {
|
|
309
|
+
try {
|
|
310
|
+
const args = staged ? ['diff', '--cached', '--name-only', '--diff-filter=ACM'] : ['ls-files'];
|
|
311
|
+
const out = execFileSync('git', args, { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
312
|
+
return out.split('\n').map((s) => s.trim()).filter(Boolean);
|
|
313
|
+
} catch {
|
|
314
|
+
return null; // not a git repo / git unavailable
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function walk(target, out) {
|
|
319
|
+
let stat;
|
|
320
|
+
try { stat = fs.statSync(target); } catch { return out; }
|
|
321
|
+
if (stat.isFile()) { out.push(target); return out; }
|
|
322
|
+
if (stat.isDirectory()) {
|
|
323
|
+
if (SKIP_DIRS.has(path.basename(target))) return out;
|
|
324
|
+
for (const name of fs.readdirSync(target)) {
|
|
325
|
+
if (name === '.git' || SKIP_DIRS.has(name)) continue;
|
|
326
|
+
walk(path.join(target, name), out);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return out;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function scannable(file) {
|
|
333
|
+
const ext = path.extname(file);
|
|
334
|
+
return SCAN_EXTS.has(ext) || path.basename(file).startsWith('.env');
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function shouldSkipRelPath(relPath) {
|
|
338
|
+
const parts = String(relPath || '').split(/[\\/]+/).filter(Boolean);
|
|
339
|
+
return parts.some((part) => SKIP_DIRS.has(part));
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Resolve the set of files to scan + the relative-path list for hygiene checks.
|
|
343
|
+
function resolveTargets({ root = process.cwd(), paths = [], staged = false } = {}) {
|
|
344
|
+
if (paths.length) {
|
|
345
|
+
const files = paths.flatMap((p) => walk(path.resolve(root, p), []))
|
|
346
|
+
.filter(scannable)
|
|
347
|
+
.filter((f) => !shouldSkipRelPath(path.relative(root, f)));
|
|
348
|
+
return { files, relPaths: files.map((f) => path.relative(root, f)) };
|
|
349
|
+
}
|
|
350
|
+
const tracked = gitTrackedFiles(root, { staged });
|
|
351
|
+
if (tracked) {
|
|
352
|
+
const relPaths = tracked.filter((p) => !shouldSkipRelPath(p));
|
|
353
|
+
const files = relPaths.filter(scannable).map((p) => path.join(root, p)).filter((f) => fs.existsSync(f));
|
|
354
|
+
return { files, relPaths };
|
|
355
|
+
}
|
|
356
|
+
const files = walk(root, []).filter(scannable).filter((f) => !shouldSkipRelPath(path.relative(root, f)));
|
|
357
|
+
return { files, relPaths: files.map((f) => path.relative(root, f)) };
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Full scan: line-level findings across files + repo hygiene findings.
|
|
361
|
+
// The scanner's own pattern database and its fixtures necessarily contain the
|
|
362
|
+
// very strings it detects — never scan them (a tool that flags itself is noise).
|
|
363
|
+
const SELF_FILES = new Set(['lib/security-scan.js', 'commands/security-review.js', 'test/security-scan.test.js']);
|
|
364
|
+
|
|
365
|
+
function runScan({ root = process.cwd(), paths = [], staged = false } = {}) {
|
|
366
|
+
const { files, relPaths } = resolveTargets({ root, paths, staged });
|
|
367
|
+
const findings = [];
|
|
368
|
+
for (const f of files) {
|
|
369
|
+
if (SELF_FILES.has(path.relative(root, f))) continue;
|
|
370
|
+
for (const finding of scanFile(f)) findings.push({ ...finding, file: path.relative(root, finding.file) });
|
|
371
|
+
}
|
|
372
|
+
findings.push(...sensitiveFileFindings(relPaths.filter((p) => !SELF_FILES.has(p))));
|
|
373
|
+
const withIds = findings.map(withFingerprint);
|
|
374
|
+
const counts = summarizeFindings(withIds);
|
|
375
|
+
return { findings: withIds, counts, scanned: files.length };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function summarizeFindings(findings) {
|
|
379
|
+
const counts = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
380
|
+
for (const f of findings) counts[f.sev] = (counts[f.sev] || 0) + 1;
|
|
381
|
+
return counts;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function loadBaseline(root = process.cwd(), baselineFile = DEFAULT_BASELINE) {
|
|
385
|
+
const file = path.isAbsolute(baselineFile) ? baselineFile : path.join(root, baselineFile);
|
|
386
|
+
if (!fs.existsSync(file)) return { file, fingerprints: [] };
|
|
387
|
+
let raw;
|
|
388
|
+
try { raw = JSON.parse(fs.readFileSync(file, 'utf8')); }
|
|
389
|
+
catch (e) { throw new Error(`invalid baseline json: ${path.relative(root, file) || file} (${e.message})`); }
|
|
390
|
+
const values = Array.isArray(raw) ? raw : raw && raw.fingerprints;
|
|
391
|
+
if (!Array.isArray(values)) throw new Error('invalid baseline json: expected {"fingerprints":[...]}');
|
|
392
|
+
return { file, fingerprints: values.filter((v) => typeof v === 'string') };
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function writeBaseline(root = process.cwd(), findings = [], baselineFile = DEFAULT_BASELINE) {
|
|
396
|
+
const file = path.isAbsolute(baselineFile) ? baselineFile : path.join(root, baselineFile);
|
|
397
|
+
const payload = {
|
|
398
|
+
generated_at: new Date().toISOString(),
|
|
399
|
+
fingerprints: [...new Set(findings.map((f) => f.fingerprint || findingFingerprint(f)))].sort(),
|
|
400
|
+
};
|
|
401
|
+
fs.writeFileSync(file, JSON.stringify(payload, null, 2) + '\n');
|
|
402
|
+
return { file, fingerprints: payload.fingerprints };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function applyBaseline(findings, fingerprints = []) {
|
|
406
|
+
const accepted = new Set(fingerprints);
|
|
407
|
+
let suppressed = 0;
|
|
408
|
+
const all = findings.map((finding) => {
|
|
409
|
+
const fp = finding.fingerprint || findingFingerprint(finding);
|
|
410
|
+
const isSuppressed = accepted.has(fp);
|
|
411
|
+
if (isSuppressed) suppressed++;
|
|
412
|
+
return { ...finding, fingerprint: fp, suppressed: isSuppressed };
|
|
413
|
+
});
|
|
414
|
+
const active = all.filter((f) => !f.suppressed);
|
|
415
|
+
return {
|
|
416
|
+
findings: active,
|
|
417
|
+
allFindings: all,
|
|
418
|
+
suppressed,
|
|
419
|
+
counts: summarizeFindings(active),
|
|
420
|
+
countsAll: summarizeFindings(all),
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function shouldFail(findings, failOn = 'high') {
|
|
425
|
+
const threshold = SEVERITY_RANK[failOn] == null ? SEVERITY_RANK.high : SEVERITY_RANK[failOn];
|
|
426
|
+
return findings.some((f) => SEVERITY_RANK[f.sev] >= threshold);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function scoreFindings(findings) {
|
|
430
|
+
const score = { low: 1, medium: 10, high: 50, critical: 100 };
|
|
431
|
+
return findings.reduce((sum, f) => sum + (score[f.sev] || 0), 0);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
module.exports = {
|
|
435
|
+
scanLine, scanText, scanFile, sensitiveFileFindings, gitTrackedFiles,
|
|
436
|
+
resolveTargets, runScan, SECRET_RULES, PII_RULES, RULES, CODE_RULES,
|
|
437
|
+
DEFAULT_BASELINE, SEVERITIES, SEVERITY_RANK, normalizeSnippet,
|
|
438
|
+
shannonEntropy, secretQuality, findingFingerprint, summarizeFindings,
|
|
439
|
+
loadBaseline, writeBaseline, applyBaseline, shouldFail, scoreFindings,
|
|
440
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atris",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.29.0",
|
|
4
4
|
"main": "bin/atris.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"atris": "bin/atris.js",
|
|
@@ -48,13 +48,6 @@
|
|
|
48
48
|
"atris/features/_templates/",
|
|
49
49
|
"atris/features/company-brain-sync/",
|
|
50
50
|
"templates/",
|
|
51
|
-
"atris/wiki/index.md",
|
|
52
|
-
"atris/wiki/concepts/agent-activation-contract.md",
|
|
53
|
-
"atris/wiki/concepts/workspace-initialization-contract.md",
|
|
54
|
-
"atris/wiki/sources/atris-labs-2026-05-10.txt",
|
|
55
|
-
"atris/wiki/sources/atris-labs-goals-2026-05-10.txt",
|
|
56
|
-
"atris/wiki/sources/atrisos-generative-ui-product-surface-2026-05-10.txt",
|
|
57
|
-
"atris/wiki/sources/jack-dorsey-2026-05-10.txt",
|
|
58
51
|
"atris/policies/",
|
|
59
52
|
"atris/skills/"
|
|
60
53
|
],
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: concept
|
|
3
|
-
slug: agent-activation-contract
|
|
4
|
-
title: Agent Activation Contract
|
|
5
|
-
sources:
|
|
6
|
-
- atris/CLAUDE.md
|
|
7
|
-
- commands/activate.js
|
|
8
|
-
last_compiled: 2026-06-23
|
|
9
|
-
last_verified: 2026-06-23
|
|
10
|
-
confidence: 0.9
|
|
11
|
-
dependencies:
|
|
12
|
-
- atris/wiki/concepts/plan-do-review-loop.md
|
|
13
|
-
- atris/wiki/concepts/wiki-as-memory-substrate.md
|
|
14
|
-
actionability: "Use this before changing agent boot instructions, `atris activate`, MAP-first behavior, first-message requirements, or durable-memory routing."
|
|
15
|
-
created: 2026-05-10
|
|
16
|
-
updated: 2026-06-23
|
|
17
|
-
tags:
|
|
18
|
-
- agent-activation
|
|
19
|
-
- protocol
|
|
20
|
-
- mapfirst
|
|
21
|
-
---
|
|
22
|
-
# Agent Activation Contract
|
|
23
|
-
|
|
24
|
-
`atris/CLAUDE.md` is the editor-facing boot contract for agents entering an Atris-managed project. `commands/activate.js` is the runtime context panel agents use after that boot. Together they define what "activation" means before responding or editing.
|
|
25
|
-
|
|
26
|
-
## Boot Sequence
|
|
27
|
-
|
|
28
|
-
```text
|
|
29
|
-
first message -> atris atris.md
|
|
30
|
-
setup -> PERSONA + activate + wiki STATUS
|
|
31
|
-
navigation -> MAP first, then one grep if needed
|
|
32
|
-
work -> plan -> do -> review
|
|
33
|
-
memory -> wiki or ingest durable knowledge
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
The first response path is explicit: run `atris atris.md`, show the welcome visualization, then answer. After that, agents load `atris/PERSONA.md`, run `atris activate`, and treat `atris/wiki/STATUS.md` as the current memory snapshot when it exists.
|
|
37
|
-
|
|
38
|
-
## Runtime Activation
|
|
39
|
-
|
|
40
|
-
`atris activate` refuses to run without an `atris/` folder and tells the operator to run `atris init` first. In a valid workspace it creates today's journal if missing, detects workspace state, loads current task context, reads `atris/wiki/STATUS.md`, and prints a narrow activation card.
|
|
41
|
-
|
|
42
|
-
The activation card can include:
|
|
43
|
-
|
|
44
|
-
- a handoff block from today's journal when `## Handoff` has structured context,
|
|
45
|
-
- the last three deduped completions from journal history,
|
|
46
|
-
- the current state summary from detected in-progress, backlog, and inbox items,
|
|
47
|
-
- a learning count from `atris/learnings.jsonl`,
|
|
48
|
-
- wiki health from `atris/wiki/STATUS.md`,
|
|
49
|
-
- core file paths for persona, MAP, task view, journal, and wiki status.
|
|
50
|
-
|
|
51
|
-
The command ends by pointing the operator back to `atris plan -> do -> review` or `atris log`. It is a read/load surface with one deliberate local side effect: ensuring the current journal file exists so the session is writable.
|
|
52
|
-
|
|
53
|
-
## MAP-First Rule
|
|
54
|
-
|
|
55
|
-
Before any file search, read `atris/MAP.md` and search the map for the target keyword. If the map has the location, go directly to that file and line. If not, grep once and update `MAP.md` so the next agent does not repeat the same scan.
|
|
56
|
-
|
|
57
|
-
## Core Truth Surfaces
|
|
58
|
-
|
|
59
|
-
- `atris/MAP.md` is navigation.
|
|
60
|
-
- `atris/TODO.md` is the visible work queue; current task ownership lives in `atris task`.
|
|
61
|
-
- `atris/logs/YYYY/YYYY-MM-DD.md` is the operating journal.
|
|
62
|
-
- `atris/wiki/STATUS.md` is the current memory snapshot.
|
|
63
|
-
- `atris/wiki/index.md` is the durable knowledge index.
|
|
64
|
-
- `atris/atris.md` is the full protocol.
|
|
65
|
-
|
|
66
|
-
## Execution Contract
|
|
67
|
-
|
|
68
|
-
Work follows `atris plan -> atris do -> atris review`. Planning requires an ASCII visualization and an approval gate. Execution is step-by-step, with verification as reality changes. Completed tasks should be removed from the active queue; the target state is zero stale tasks.
|
|
69
|
-
|
|
70
|
-
Durable project knowledge belongs in `atris/wiki/` or through the local wiki flow. Ephemeral progress belongs in task state and the daily journal, not in ad hoc context.
|
|
71
|
-
|
|
72
|
-
`atris/CLAUDE.md` now also carries an explicit agent contract: before edits, claim or create one small task with `atris task` and write the goal/files/done/check contract into task dialogue; after edits, move proof-backed work to Review with `atris task ready <id> --proof "..."`. Proof-ready and human accept are separate gates: an agent's native goal can complete once proof is in Review, but only a human-run `atris task accept <id>` marks the task Done and awards AgentXP. Work that should outlive the chat runs through `atris mission` (start with a verifier, bounded step, `mission tick --verify`, then complete or continue).
|
|
73
|
-
|
|
74
|
-
## Limits
|
|
75
|
-
|
|
76
|
-
This page summarizes the activation contract only. Use `atris/atris.md` for the complete protocol, `atris/wiki/concepts/plan-do-review-loop.md` for stage ownership and proof, and `atris/wiki/concepts/wiki-as-memory-substrate.md` for wiki page shape and upkeep behavior. `atris activate` reports state; it does not claim tasks, finish work, or repair broken wiki pages.
|
|
77
|
-
|
|
78
|
-
## Cross-References
|
|
79
|
-
|
|
80
|
-
- [[atris/wiki/concepts/plan-do-review-loop.md]] - stage ownership, proof, and review closure
|
|
81
|
-
- [[atris/wiki/concepts/wiki-as-memory-substrate.md]] - durable memory routing and wiki verification
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: concept
|
|
3
|
-
slug: workspace-initialization-contract
|
|
4
|
-
title: Workspace Initialization Contract
|
|
5
|
-
sources:
|
|
6
|
-
- commands/init.js
|
|
7
|
-
last_compiled: 2026-06-23
|
|
8
|
-
last_verified: 2026-06-23
|
|
9
|
-
confidence: 0.86
|
|
10
|
-
dependencies:
|
|
11
|
-
- atris/wiki/systems/atris-cli.md
|
|
12
|
-
- atris/wiki/concepts/agent-activation-contract.md
|
|
13
|
-
- atris/wiki/concepts/wiki-as-memory-substrate.md
|
|
14
|
-
actionability: "Use this before changing `atris init`, workspace scaffolds, generated agent instructions, project detection, or boot hook behavior."
|
|
15
|
-
created: 2026-05-10
|
|
16
|
-
updated: 2026-06-23
|
|
17
|
-
tags:
|
|
18
|
-
- initialization
|
|
19
|
-
- workspace
|
|
20
|
-
- scaffold
|
|
21
|
-
---
|
|
22
|
-
# Workspace Initialization Contract
|
|
23
|
-
|
|
24
|
-
`commands/init.js` defines the local contract for turning an arbitrary folder into an Atris-managed workspace. It is the repo-level bootstrap path, not the business workspace onboarding path.
|
|
25
|
-
|
|
26
|
-
## Shape
|
|
27
|
-
|
|
28
|
-
```text
|
|
29
|
-
guard workspace -> create atris/ -> scaffold memory + teams + features
|
|
30
|
-
-> detect project -> inject team context
|
|
31
|
-
-> generate agent entry files + hooks
|
|
32
|
-
-> copy atris protocol
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
The command handles `atris init [--force]`. Help flags print usage without filesystem side effects. Normal execution refuses to run inside an existing `atris/` directory, inside a parent business workspace, or in a folder with `.atris/business.json` unless `--force` is present.
|
|
36
|
-
|
|
37
|
-
## What Init Creates
|
|
38
|
-
|
|
39
|
-
- Core workspace: `atris/`, `atris/atris.md`, `atris/PERSONA.md`, `atris/GETTING_STARTED.md`, `atris/MAP.md`, `atris/TODO.md`, `atris/now.md`, `atris/lessons.md`, and dated logs.
|
|
40
|
-
- Memory surfaces: wiki scaffold via `ensureWikiScaffold`, feature templates, experiments harness, and `INTUITION.md`.
|
|
41
|
-
- Team surfaces: `atris/team/<member>/MEMBER.md` plus `skills/`, `tools/`, and `context/` folders for default members.
|
|
42
|
-
- Project profile: `.project-profile.json` from package files, framework hints, directory shape, and default test command.
|
|
43
|
-
- Agent entry files: `AGENTS.md`, `.cursorrules`, `.cursor/rules/atris.mdc`, `.claude/commands/atris.md`, `.claude/commands/atris-autopilot.md`, `atris/CLAUDE.md`, `.claude/settings.json`, `.devin/config.local.json`, and root `CLAUDE.md` Atris markers.
|
|
44
|
-
- Skills: package `atris/skills/` copied into the workspace and linked into `.claude/skills/` when possible.
|
|
45
|
-
|
|
46
|
-
## Project Detection
|
|
47
|
-
|
|
48
|
-
`detectProjectContext()` scans package files first, then framework-specific dependencies, then common structure directories. It detects Node, Python, Ruby, Go, Rust, Java, PHP, Elixir, D, iOS, and markdown-only knowledge bases. The resulting test command is a default hint (`npm test`, `pytest`, `go test ./...`, etc.), not a guarantee that validation is sufficient.
|
|
49
|
-
|
|
50
|
-
`injectProjectPatterns()` writes that profile into navigator, executor, and validator specs so the first generated team has a local project shape before any agent starts work.
|
|
51
|
-
|
|
52
|
-
## Generated Agent Contract
|
|
53
|
-
|
|
54
|
-
The generated instruction files all point agents back to the same small operating loop:
|
|
55
|
-
|
|
56
|
-
- run the Atris boot sequence before first response,
|
|
57
|
-
- keep replies short,
|
|
58
|
-
- use ASCII visuals for planning,
|
|
59
|
-
- check `MAP.md` before code search,
|
|
60
|
-
- use `atris task` for ownership and proof,
|
|
61
|
-
- treat `TODO.md` as rendered state, not the task database.
|
|
62
|
-
|
|
63
|
-
Claude-specific setup also adds a `SessionStart` hook that runs `atris atris.md` when `atris/` exists and a `Stop` hook that points to the autopilot stop hook.
|
|
64
|
-
|
|
65
|
-
## Limits
|
|
66
|
-
|
|
67
|
-
`atris init` bootstraps local files. It does not push to cloud, create a shared business owner, or reconcile template updates after custom edits. Use business commands for shared-owner workspaces and `atris update` / sync flows for canonical file refreshes.
|
|
68
|
-
|
|
69
|
-
## Cross-References
|
|
70
|
-
|
|
71
|
-
- [[atris/wiki/concepts/agent-activation-contract.md]] - the boot behavior generated agent files point to
|
|
72
|
-
- [[atris/wiki/concepts/wiki-as-memory-substrate.md]] - the wiki scaffold and memory contract initialized by this command
|
|
73
|
-
- [[atris/wiki/systems/atris-cli.md]] - repo-level command surface that includes `atris init`
|
package/atris/wiki/index.md
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# Atris Wiki Index
|
|
2
|
-
|
|
3
|
-
## Entities
|
|
4
|
-
|
|
5
|
-
- [[atris/wiki/people/jack-dorsey.md]] — Block / Twitter founder; AI-native company thesis
|
|
6
|
-
- [[atris/wiki/systems/atris-cli.md]] — this project; dev-tool layer of the Atris stack
|
|
7
|
-
- [[atris/wiki/systems/atris-business.md]] — sibling product; shared owners with persistent AI computers
|
|
8
|
-
- [[atris/wiki/systems/atris-labs.md]] — reference business owner in the Atris fleet; default computer dogfood
|
|
9
|
-
|
|
10
|
-
## Concepts
|
|
11
|
-
|
|
12
|
-
- [[atris/wiki/concepts/intent-capability-composition.md]] — the operating loop; roadmap from gaps
|
|
13
|
-
- [[atris/wiki/concepts/wiki-as-memory-substrate.md]] — what `atris/wiki/` is and isn't
|
|
14
|
-
- [[atris/wiki/concepts/plan-do-review-loop.md]] — core Atris workflow and how local memory fits into it
|
|
15
|
-
- [[atris/wiki/concepts/rebased-pack-co-first-loop.md]] — local-only business workspace first loop and proof guardrails
|
|
16
|
-
- [[atris/wiki/concepts/atris-labs-goals.md]] — atris-labs north star, 2026 Q2 targets, standing constraints
|
|
17
|
-
- [[atris/wiki/concepts/horizon-types.md]] — horizon slug prefix convention; type categories and inference rules
|
|
18
|
-
- [[atris/wiki/concepts/verifiable-reward-loop.md]] — reward, scorecards, and why the repo now acts like an RL-style environment
|
|
19
|
-
- [[atris/wiki/concepts/recursive-self-improvement-position.md]] — honest capability assessment: the loop is the RSI skeleton on the leverage axis, intelligence axis fixed by the model
|
|
20
|
-
- [[atris/wiki/concepts/owner-computer-model.md]] — Owner = User | Business; constrained entity modes, typed computers, and groups as the social/access layer
|
|
21
|
-
- [[atris/wiki/concepts/agent-activation-contract.md]] — editor-facing boot contract: first message, MAP-first, setup, and durable-memory routing
|
|
22
|
-
- [[atris/wiki/concepts/workspace-initialization-contract.md]] — `atris init` bootstrap contract: guards, scaffolds, team context, generated agent files, and hooks
|
|
23
|
-
- [[atris/wiki/concepts/glass-interface-principle.md]] — AI tool design doctrine: make reasoning visible and inspectable, not black-boxed
|
|
24
|
-
|
|
25
|
-
## Briefs
|
|
26
|
-
|
|
27
|
-
- [[atris/wiki/briefs/rebased-pack-co-starter-brief.md]] — starter brief for the local-only Rebased Pack Co smoke workspace
|
|
28
|
-
- [[atris/wiki/briefs/atris-cli-overview.md]] — summary of CLI, owner/computer model, workspace layers, and why `atris/wiki/` exists
|
|
29
|
-
- [[atris/wiki/briefs/atris-labs-workspace-protocol.md]] — atris-labs workspace protocol: on-load sequence, layout, surfaces, north star
|
|
30
|
-
- [[atris/wiki/briefs/atrisos-generative-ui-product-surface.md]] — historical AtrisOS generative UI / block surface design note
|
|
31
|
-
- [[atris/wiki/briefs/launch-post.md]] — historical launch drafts for the local-first wiki feature
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
Source receipt for atris/wiki/systems/atris-labs.md
|
|
2
|
-
Compiled: 2026-05-10
|
|
3
|
-
|
|
4
|
-
Local evidence checked:
|
|
5
|
-
- historical Atris Labs customer workspace docs
|
|
6
|
-
- historical Atris Labs context status and pipeline notes
|
|
7
|
-
- historical Atris Labs member profile notes
|
|
8
|
-
- historical Atris Labs app workspace inventory
|
|
9
|
-
- historical Atris Labs cloud workspace binding metadata
|
|
10
|
-
|
|
11
|
-
Notes:
|
|
12
|
-
- The previous wiki source is missing in this workspace.
|
|
13
|
-
- Available status/pipeline files are historical March 2026 artifacts.
|
|
14
|
-
- Treat this page as orientation, not a live company operating report.
|