company-skill 4.5.0 → 4.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/agents/company-worker.md +2 -0
- package/bin/install.js +0 -9
- package/hooks/precompact.js +7 -13
- package/hooks/session-restore.js +7 -13
- package/install.sh +5 -6
- package/package.json +2 -2
- package/scripts/check-contracts.js +5 -6
- package/scripts/check-findings.js +3 -4
- package/tests/stop-guard.test.js +12 -14
package/README.md
CHANGED
|
@@ -27,17 +27,17 @@ Optionally define your team first in `COMPANY.md` (skip it and a minimal company
|
|
|
27
27
|
```mermaid
|
|
28
28
|
graph LR
|
|
29
29
|
G[GOAL] --> T[THINK]
|
|
30
|
-
T -->|contract shape gate| E[EXECUTE]
|
|
30
|
+
T -->|contract shape gate| E[EXECUTE in dependency waves]
|
|
31
31
|
E -->|findings shape gate| V[VERIFY]
|
|
32
32
|
V -->|reviewer re-derives + critic attacks| D{Done?}
|
|
33
33
|
D -->|NO: feedback| C[COMPRESS]
|
|
34
34
|
C --> T
|
|
35
|
-
D -->|YES| S[STATUS.md]
|
|
36
|
-
D -.->|stop
|
|
35
|
+
D -->|YES| S[STATUS.md + playbook]
|
|
36
|
+
D -.->|stop blocked with the goal and failing notes| B[stop guard]
|
|
37
37
|
B -.-> T
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
It runs any model the
|
|
40
|
+
It runs any model with the operating rigor the frontier pair Claude Fable 5 and Mythos demonstrate: delegation contracts, two-pass evidence verification, and failing-by-default criteria ship as structural artifacts, so the discipline holds whichever model fills each role. The orchestrator reads the goal and activates only the relevant employees. Leads decompose the goal into delegation contracts, workers execute them in parallel waves, and two reviewers gate every cycle: the Internal Reviewer re-runs the evidence and the Devil's Advocate attacks it. There is no iteration limit. The harness carries the quality, so none of it depends on the model remembering to be careful.
|
|
41
41
|
|
|
42
42
|
## Delegation contracts
|
|
43
43
|
|
package/agents/company-worker.md
CHANGED
|
@@ -33,6 +33,8 @@ Rate each finding's importance 1-5 (the digest keeps 4-5 in full).
|
|
|
33
33
|
|
|
34
34
|
Report SHORT. Result first, then the evidence (FINDING + SOURCE: the command and its output, the file, the PR/SHA/CI link). No narration of your steps, no restating the task. Concise never means unsourced: cut the prose around a claim, never the source that proves it.
|
|
35
35
|
|
|
36
|
+
Narrate intent before consequential tool calls: one short line on what you are about to do and why. A silent agent is far harder for the verify layers to audit, and the audit trail is the product.
|
|
37
|
+
|
|
36
38
|
Anything a human reads outside the run (a PR body, a comment, an email, a post) gets a /humanizer pass before you publish it: short, professional, human-sounding. Evidence lines stay verbatim. If the skill is missing, self-edit to the same bar and note SKILL-MISSING.
|
|
37
39
|
|
|
38
40
|
End every findings append with one machine-greppable line: `STATUS: complete` when DONE-WHEN is met and verified, `STATUS: blocked` with the blocker named above it, or `STATUS: incomplete` with what remains. The orchestrator greps this line instead of parsing your prose.
|
package/bin/install.js
CHANGED
|
@@ -16,22 +16,18 @@ function copyFile(src, dest) {
|
|
|
16
16
|
fs.copyFileSync(src, dest);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
// Install skill
|
|
20
19
|
copyFile(path.join(srcDir, 'skill', 'SKILL.md'), path.join(skillDir, 'SKILL.md'));
|
|
21
20
|
|
|
22
|
-
// Install commands
|
|
23
21
|
for (const cmd of ['run', 'status', 'resume']) {
|
|
24
22
|
const src = path.join(srcDir, 'commands', `${cmd}.md`);
|
|
25
23
|
if (fs.existsSync(src)) copyFile(src, path.join(commandsDir, `${cmd}.md`));
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
// Install agents
|
|
29
26
|
for (const agent of ['lead', 'worker', 'reviewer', 'critic', 'digest']) {
|
|
30
27
|
const src = path.join(srcDir, 'agents', `company-${agent}.md`);
|
|
31
28
|
if (fs.existsSync(src)) copyFile(src, path.join(agentsDir, `company-${agent}.md`));
|
|
32
29
|
}
|
|
33
30
|
|
|
34
|
-
// Install all hooks
|
|
35
31
|
const hookFiles = {
|
|
36
32
|
'stop-guard.js': 'company-stop-guard.js',
|
|
37
33
|
'precompact.js': 'company-precompact.js',
|
|
@@ -52,19 +48,16 @@ try {
|
|
|
52
48
|
|
|
53
49
|
if (!settings.hooks) settings.hooks = {};
|
|
54
50
|
|
|
55
|
-
// Stop hook
|
|
56
51
|
if (!settings.hooks.Stop) settings.hooks.Stop = [];
|
|
57
52
|
if (!settings.hooks.Stop.some(h => h.hooks?.some(hh => hh.command?.includes('company-stop-guard')))) {
|
|
58
53
|
settings.hooks.Stop.push({ hooks: [{ type: 'command', command: `node "${path.join(hooksDir, 'company-stop-guard.js')}"`, timeout: 10 }] });
|
|
59
54
|
}
|
|
60
55
|
|
|
61
|
-
// PreCompact hook
|
|
62
56
|
if (!settings.hooks.PreCompact) settings.hooks.PreCompact = [];
|
|
63
57
|
if (!settings.hooks.PreCompact.some(h => h.hooks?.some(hh => hh.command?.includes('company-precompact')))) {
|
|
64
58
|
settings.hooks.PreCompact.push({ hooks: [{ type: 'command', command: `node "${path.join(hooksDir, 'company-precompact.js')}"`, timeout: 10 }] });
|
|
65
59
|
}
|
|
66
60
|
|
|
67
|
-
// SessionStart hook (compact restore)
|
|
68
61
|
if (!settings.hooks.SessionStart) settings.hooks.SessionStart = [];
|
|
69
62
|
if (!settings.hooks.SessionStart.some(h => h.hooks?.some(hh => hh.command?.includes('company-session-restore')))) {
|
|
70
63
|
settings.hooks.SessionStart.push({ matcher: 'compact', hooks: [{ type: 'command', command: `node "${path.join(hooksDir, 'company-session-restore.js')}"`, timeout: 10 }] });
|
|
@@ -79,7 +72,6 @@ try {
|
|
|
79
72
|
console.log('Could not register hooks. Add manually to settings.json.');
|
|
80
73
|
}
|
|
81
74
|
|
|
82
|
-
// Create COMPANY.md template
|
|
83
75
|
const companyMd = path.join(process.cwd(), 'COMPANY.md');
|
|
84
76
|
const template = path.join(srcDir, 'COMPANY.md.template');
|
|
85
77
|
if (!fs.existsSync(companyMd) && fs.existsSync(template)) {
|
|
@@ -87,7 +79,6 @@ if (!fs.existsSync(companyMd) && fs.existsSync(template)) {
|
|
|
87
79
|
console.log('Created COMPANY.md template.');
|
|
88
80
|
}
|
|
89
81
|
|
|
90
|
-
// Gitignore
|
|
91
82
|
try {
|
|
92
83
|
const gi = path.join(process.cwd(), '.gitignore');
|
|
93
84
|
const content = fs.existsSync(gi) ? fs.readFileSync(gi, 'utf8') : '';
|
package/hooks/precompact.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
//
|
|
3
|
+
// Saves company state before context compaction so session-restore can rebuild
|
|
4
|
+
// the model's context. Snapshots goal, cycle, briefing, review, criteria, roster,
|
|
5
|
+
// and the TAIL of the playbook (new entries append at the bottom).
|
|
5
6
|
|
|
6
7
|
const fs = require('fs');
|
|
7
8
|
const path = require('path');
|
|
@@ -9,9 +10,9 @@ const path = require('path');
|
|
|
9
10
|
const companyDir = process.env.COMPANY_DIR || path.join(process.cwd(), '.company');
|
|
10
11
|
if (!fs.existsSync(companyDir)) process.exit(0);
|
|
11
12
|
|
|
12
|
-
// Only sessions
|
|
13
|
-
//
|
|
14
|
-
//
|
|
13
|
+
// Only sessions listed in OWNER are acted on. A foreign session that shares the
|
|
14
|
+
// directory must not have state written on its behalf. Missing or empty OWNER
|
|
15
|
+
// keeps the old behavior (act on all sessions).
|
|
15
16
|
try {
|
|
16
17
|
const hookInput = JSON.parse(fs.readFileSync(0, 'utf8'));
|
|
17
18
|
if (hookInput && typeof hookInput.session_id === 'string') {
|
|
@@ -23,7 +24,6 @@ try {
|
|
|
23
24
|
|
|
24
25
|
const lines = ['# Company Checkpoint (auto-saved before compaction)', ''];
|
|
25
26
|
|
|
26
|
-
// Goal
|
|
27
27
|
const goalPath = path.join(companyDir, 'GOAL.md');
|
|
28
28
|
if (fs.existsSync(goalPath)) {
|
|
29
29
|
lines.push('## Goal');
|
|
@@ -31,7 +31,6 @@ if (fs.existsSync(goalPath)) {
|
|
|
31
31
|
lines.push('');
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
// Cycle number
|
|
35
34
|
const cyclesDir = path.join(companyDir, 'cycles');
|
|
36
35
|
if (fs.existsSync(cyclesDir)) {
|
|
37
36
|
const files = fs.readdirSync(cyclesDir).filter(f => f.startsWith('cycle-'));
|
|
@@ -40,7 +39,6 @@ if (fs.existsSync(cyclesDir)) {
|
|
|
40
39
|
lines.push('## Cycle: ' + cycle);
|
|
41
40
|
lines.push('');
|
|
42
41
|
|
|
43
|
-
// Latest briefing (captures current reasoning/intent)
|
|
44
42
|
const briefing = path.join(cyclesDir, `cycle-${cycle}-briefing.md`);
|
|
45
43
|
if (fs.existsSync(briefing)) {
|
|
46
44
|
lines.push('## Current Reasoning');
|
|
@@ -48,7 +46,6 @@ if (fs.existsSync(cyclesDir)) {
|
|
|
48
46
|
lines.push('');
|
|
49
47
|
}
|
|
50
48
|
|
|
51
|
-
// Latest review (captures what's working/failing)
|
|
52
49
|
const review = path.join(cyclesDir, `cycle-${cycle}-review.md`);
|
|
53
50
|
if (fs.existsSync(review)) {
|
|
54
51
|
lines.push('## Latest Review');
|
|
@@ -57,7 +54,6 @@ if (fs.existsSync(cyclesDir)) {
|
|
|
57
54
|
}
|
|
58
55
|
}
|
|
59
56
|
|
|
60
|
-
// Criteria status
|
|
61
57
|
const criteriaPath = path.join(companyDir, 'criteria.json');
|
|
62
58
|
if (fs.existsSync(criteriaPath)) {
|
|
63
59
|
try {
|
|
@@ -71,7 +67,6 @@ if (fs.existsSync(criteriaPath)) {
|
|
|
71
67
|
} catch (e) {}
|
|
72
68
|
}
|
|
73
69
|
|
|
74
|
-
// Active roster
|
|
75
70
|
const rosterPath = path.join(companyDir, 'active-roster.md');
|
|
76
71
|
if (fs.existsSync(rosterPath)) {
|
|
77
72
|
lines.push('## Active Roster');
|
|
@@ -79,8 +74,7 @@ if (fs.existsSync(rosterPath)) {
|
|
|
79
74
|
lines.push('');
|
|
80
75
|
}
|
|
81
76
|
|
|
82
|
-
//
|
|
83
|
-
// bottom, so snapshot the TAIL, not the head.
|
|
77
|
+
// New playbook entries append at the bottom, so snapshot the tail, not the head.
|
|
84
78
|
const playbookPath = path.join(companyDir, 'playbook.md');
|
|
85
79
|
if (fs.existsSync(playbookPath)) {
|
|
86
80
|
const playbook = fs.readFileSync(playbookPath, 'utf8');
|
package/hooks/session-restore.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// SessionStart context must go through hookSpecificOutput.additionalContext.
|
|
10
|
-
// That field reaches the model. systemMessage is a user-facing display only and
|
|
11
|
-
// never enters the model's context.
|
|
3
|
+
// Restores company context after compaction and instructs the model to run the
|
|
4
|
+
// /company restart procedure before continuing.
|
|
5
|
+
// Context must go through hookSpecificOutput.additionalContext - that field
|
|
6
|
+
// reaches the model. systemMessage is user-facing display only and never enters
|
|
7
|
+
// the model's context.
|
|
12
8
|
|
|
13
9
|
const fs = require('fs');
|
|
14
10
|
const path = require('path');
|
|
@@ -16,9 +12,8 @@ const path = require('path');
|
|
|
16
12
|
const companyDir = process.env.COMPANY_DIR || path.join(process.cwd(), '.company');
|
|
17
13
|
if (!fs.existsSync(companyDir)) process.exit(0);
|
|
18
14
|
|
|
19
|
-
// Only sessions
|
|
20
|
-
//
|
|
21
|
-
// behalf. Missing or empty OWNER is legacy state and keeps the old behavior.
|
|
15
|
+
// Only sessions listed in OWNER are acted on. A foreign session that shares the
|
|
16
|
+
// directory must not be redirected. Missing or empty OWNER keeps the old behavior.
|
|
22
17
|
try {
|
|
23
18
|
const hookInput = JSON.parse(fs.readFileSync(0, 'utf8'));
|
|
24
19
|
if (hookInput && typeof hookInput.session_id === 'string') {
|
|
@@ -34,7 +29,6 @@ if (fs.existsSync(checkpointMd)) {
|
|
|
34
29
|
state = fs.readFileSync(checkpointMd, 'utf8').substring(0, 2000);
|
|
35
30
|
}
|
|
36
31
|
|
|
37
|
-
// The post-compaction directive: run the restart procedure, do not just "continue".
|
|
38
32
|
const directive =
|
|
39
33
|
'[COMPANY] Context was compacted, so prior turn-by-turn state is gone. Before doing ' +
|
|
40
34
|
'anything else, run the /company restart procedure from the skill: refresh ' +
|
package/install.sh
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Installs the /company skill, commands, agents,
|
|
3
|
-
# re-running overwrites
|
|
2
|
+
# Installs the /company skill, commands, agents, and hooks.
|
|
3
|
+
# Idempotent: re-running overwrites copied files and never duplicates hook entries.
|
|
4
4
|
set -e
|
|
5
5
|
|
|
6
6
|
REPO="https://raw.githubusercontent.com/jagmarques/company-skill/main"
|
|
@@ -33,7 +33,7 @@ for agent in lead worker reviewer critic digest; do
|
|
|
33
33
|
fetch "$REPO/agents/company-$agent.md" "$HOME/.claude/agents/company-$agent.md" || echo "Warning: failed to download agent company-$agent"
|
|
34
34
|
done
|
|
35
35
|
|
|
36
|
-
# Hooks
|
|
36
|
+
# Hooks
|
|
37
37
|
mkdir -p "$HOME/.claude/hooks"
|
|
38
38
|
for pair in "stop-guard company-stop-guard" "precompact company-precompact" "session-restore company-session-restore"; do
|
|
39
39
|
src="${pair%% *}"
|
|
@@ -41,9 +41,8 @@ for pair in "stop-guard company-stop-guard" "precompact company-precompact" "ses
|
|
|
41
41
|
fetch "$REPO/hooks/$src.js" "$HOME/.claude/hooks/$dest.js" || echo "Warning: failed to download hook $src"
|
|
42
42
|
done
|
|
43
43
|
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
# steps otherwise. The merge is idempotent: existing entries are kept.
|
|
44
|
+
# JSON editing from shell is unsafe without a JSON tool. Use node when available
|
|
45
|
+
# and print exact manual steps otherwise. The merge is idempotent.
|
|
47
46
|
if command -v node >/dev/null 2>&1; then
|
|
48
47
|
node <<'EOF'
|
|
49
48
|
const fs = require('fs');
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "company-skill",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.1",
|
|
4
4
|
"description": "Goal-driven multi-employee company for Claude Code. Give it a goal, it runs until done.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"company-skill": "./bin/install.js"
|
|
7
7
|
},
|
|
8
|
-
"keywords": ["claude-code", "skill", "multi-agent", "company", "orchestration", "model-agnostic", "autonomous-agents", "stop-hook", "agent-orchestration", "ai-agents", "fable", "claude-fable"],
|
|
8
|
+
"keywords": ["claude-code", "skill", "multi-agent", "company", "orchestration", "model-agnostic", "autonomous-agents", "stop-hook", "agent-orchestration", "ai-agents", "fable", "claude-fable", "mythos", "claude-mythos"],
|
|
9
9
|
"author": "jagmarques",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"repository": {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// Exit 1 lists each defective contract. No fields, no task.
|
|
2
|
+
// Gate for delegation contracts: every TASK block must carry all seven fields
|
|
3
|
+
// and a non-empty VERIFY-WITH. Exit 1 lists each defective contract.
|
|
4
|
+
// Run: node scripts/check-contracts.js .company/cycles/cycle-N-tasks.md
|
|
6
5
|
const fs = require('fs');
|
|
7
6
|
const file = process.argv[2];
|
|
8
7
|
if (!file || !fs.existsSync(file)) { console.error('usage: check-contracts.js <tasks-file>'); process.exit(2); }
|
|
@@ -10,8 +9,8 @@ const text = fs.readFileSync(file, 'utf8');
|
|
|
10
9
|
const blocks = text.split(/\n(?=TASK:)/).filter(b => b.trim().startsWith('TASK:'));
|
|
11
10
|
if (blocks.length === 0) { console.error('no TASK blocks found'); process.exit(1); }
|
|
12
11
|
const FIELDS = ['TASK:', 'EMPLOYEE:', 'SKILL:', 'INPUTS:', 'OUTPUT:', 'DONE-WHEN:', 'VERIFY-WITH:', 'OUT-OF-SCOPE:'];
|
|
13
|
-
// DEPENDS-ON is optional. When present it must name existing task numbers
|
|
14
|
-
//
|
|
12
|
+
// DEPENDS-ON is optional. When present it must name existing task numbers and the
|
|
13
|
+
// dependency graph must be acyclic.
|
|
15
14
|
function checkDeps(blocks) {
|
|
16
15
|
const errs = [];
|
|
17
16
|
const deps = blocks.map((b, i) => {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// Exit 1 names each unsourced finding. A claim without a source is unverifiable.
|
|
2
|
+
// Gate for findings files: every FINDING line must be followed by a SOURCE
|
|
3
|
+
// line (or NOVEL marker) before the next FINDING. A claim without a source
|
|
4
|
+
// is unverifiable. Run: node scripts/check-findings.js .company/<dept>/<employee>.md [...]
|
|
6
5
|
const fs = require('fs');
|
|
7
6
|
const files = process.argv.slice(2);
|
|
8
7
|
if (!files.length) { console.error('usage: check-findings.js <findings-file...>'); process.exit(2); }
|
package/tests/stop-guard.test.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// Decision-logic matrix for hooks/stop-guard.js.
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// The CI floor (scripts/check.sh) runs this file, so a future edit cannot
|
|
8
|
-
// regress the fail-closed behavior with CI still green.
|
|
3
|
+
// Decision-logic matrix for hooks/stop-guard.js. Builds a fixture dir per case,
|
|
4
|
+
// runs the hook with COMPANY_DIR pointed at it, and asserts allow/block from
|
|
5
|
+
// stdout. A block emits JSON with decision "block"; allow exits 0 with no output.
|
|
6
|
+
// Included in scripts/check.sh so a future edit cannot silently regress fail-closed.
|
|
9
7
|
|
|
10
8
|
const { execFileSync } = require('child_process');
|
|
11
9
|
const fs = require('fs');
|
|
@@ -65,13 +63,13 @@ function writeCriteria(dir, value) {
|
|
|
65
63
|
typeof value === 'string' ? value : JSON.stringify(value));
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
// 1. No company state
|
|
66
|
+
// 1. No company state: hook must allow silently.
|
|
69
67
|
{
|
|
70
68
|
const d = freshDir();
|
|
71
69
|
check('no state allows', d, 'allow');
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
// 2. CANCEL file
|
|
72
|
+
// 2. CANCEL file allows and must be consumed so it cannot leak into a later run.
|
|
75
73
|
{
|
|
76
74
|
const d = freshDir();
|
|
77
75
|
fs.writeFileSync(path.join(d, 'GOAL.md'), 'goal');
|
|
@@ -114,14 +112,14 @@ function writeCriteria(dir, value) {
|
|
|
114
112
|
check('failing criterion blocks', d, 'block', 'ship it');
|
|
115
113
|
}
|
|
116
114
|
|
|
117
|
-
// 7. passes
|
|
115
|
+
// 7. passes:true without evidence still blocks; evidence is the contract.
|
|
118
116
|
{
|
|
119
117
|
const d = freshDir();
|
|
120
118
|
writeCriteria(d, { criteria: [{ id: 1, description: 'ev gap', passes: true, evidence: null }] });
|
|
121
119
|
check('passes without evidence blocks', d, 'block', 'ev gap');
|
|
122
120
|
}
|
|
123
121
|
|
|
124
|
-
// 8. All passing with evidence allows the stop.
|
|
122
|
+
// 8. All criteria passing with evidence allows the stop.
|
|
125
123
|
{
|
|
126
124
|
const d = freshDir();
|
|
127
125
|
writeCriteria(d, { criteria: [
|
|
@@ -154,7 +152,7 @@ function writeCriteria(dir, value) {
|
|
|
154
152
|
check('stale still blocks with age note', d, 'block', 'untouched for');
|
|
155
153
|
}
|
|
156
154
|
|
|
157
|
-
// 12.
|
|
155
|
+
// 12. A foreign session passes when OWNER names a different session.
|
|
158
156
|
{
|
|
159
157
|
const d = freshDir();
|
|
160
158
|
writeCriteria(d, { criteria: [{ id: 1, description: 'a', passes: false, evidence: null }] });
|
|
@@ -201,7 +199,7 @@ function writeCriteria(dir, value) {
|
|
|
201
199
|
{ input: JSON.stringify({ session_id: 'owner-eee' }) });
|
|
202
200
|
}
|
|
203
201
|
|
|
204
|
-
// 17. Deleting a locked criterion blocks even when everything
|
|
202
|
+
// 17. Deleting a locked criterion blocks even when everything remaining passes.
|
|
205
203
|
{
|
|
206
204
|
const d = freshDir();
|
|
207
205
|
writeCriteria(d, { criteria: [
|
|
@@ -213,7 +211,7 @@ function writeCriteria(dir, value) {
|
|
|
213
211
|
check('deleting a locked criterion blocks', d, 'block', 'locked criterion');
|
|
214
212
|
}
|
|
215
213
|
|
|
216
|
-
// 18. Adding a criterion extends the lock
|
|
214
|
+
// 18. Adding a criterion extends the lock; a fully passing set allows.
|
|
217
215
|
{
|
|
218
216
|
const d = freshDir();
|
|
219
217
|
writeCriteria(d, { criteria: [
|
|
@@ -233,7 +231,7 @@ function writeCriteria(dir, value) {
|
|
|
233
231
|
}
|
|
234
232
|
}
|
|
235
233
|
|
|
236
|
-
// 19.
|
|
234
|
+
// 19. The block reason must not reveal the cancel command to the model.
|
|
237
235
|
{
|
|
238
236
|
const d = freshDir();
|
|
239
237
|
writeCriteria(d, { criteria: [{ id: 1, description: 'a', passes: false, evidence: null }] });
|