instar 1.3.756 → 1.3.758
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/dist/core/PostUpdateMigrator.d.ts +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +182 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/instarSettingsHooks.d.ts.map +1 -1
- package/dist/core/instarSettingsHooks.js +12 -0
- package/dist/core/instarSettingsHooks.js.map +1 -1
- package/dist/core/types.d.ts +16 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/scheduler/AgentMdJobLoader.d.ts +4 -0
- package/dist/scheduler/AgentMdJobLoader.d.ts.map +1 -1
- package/dist/scheduler/AgentMdJobLoader.js +2 -0
- package/dist/scheduler/AgentMdJobLoader.js.map +1 -1
- package/dist/scheduler/InstallBuiltinJobs.d.ts.map +1 -1
- package/dist/scheduler/InstallBuiltinJobs.js +3 -0
- package/dist/scheduler/InstallBuiltinJobs.js.map +1 -1
- package/dist/scheduler/JobScheduler.d.ts.map +1 -1
- package/dist/scheduler/JobScheduler.js +17 -5
- package/dist/scheduler/JobScheduler.js.map +1 -1
- package/dist/scheduler/buildPerSlugManifest.d.ts +4 -0
- package/dist/scheduler/buildPerSlugManifest.d.ts.map +1 -1
- package/dist/scheduler/buildPerSlugManifest.js +2 -0
- package/dist/scheduler/buildPerSlugManifest.js.map +1 -1
- package/package.json +1 -1
- package/scripts/doorway-scan.mjs +785 -0
- package/scripts/model-registry-freshness.manifest.json +17 -2
- package/src/data/builtin-manifest.json +20 -20
- package/src/scaffold/templates/jobs/instar/doorway-scan.md +32 -0
- package/upgrades/1.3.757.md +29 -0
- package/upgrades/1.3.758.md +37 -0
- package/upgrades/side-effects/doorway-model-registry-inc2.md +96 -0
- package/upgrades/side-effects/keyword-lint-enforce.md +113 -0
- package/dist/core/AutonomySkill.d.ts +0 -98
- package/dist/core/AutonomySkill.d.ts.map +0 -1
- package/dist/core/AutonomySkill.js +0 -497
- package/dist/core/AutonomySkill.js.map +0 -1
- package/dist/core/TopicClassifier.d.ts +0 -54
- package/dist/core/TopicClassifier.d.ts.map +0 -1
- package/dist/core/TopicClassifier.js +0 -144
- package/dist/core/TopicClassifier.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "Doorway/Model Knowledge Registry — canonical/reviewed layer + staleness/drift gate for Instar's per-provider 'capable/latest' model pins. This file is the SINGLE human-edit surface that keeps the model registry from silently rotting. See docs/LLM-ROUTING-REGISTRY.md, docs/specs/DOORWAY-MODEL-KNOWLEDGE-REGISTRY-SPEC.md, and scripts/lint-model-registry-freshness.mjs. Two teeth: (1) STALENESS — lastReviewedAt must be within stalenessWindowDays; (2) DRIFT — every pinned capable model id must be a member of its door's DERIVED frontier set (the ids in doors[door].topModels[] carrying frontier:true — see §1.4 of the spec). topModels is the ONE hand-edited source of truth; the frontier set is a view of it and cannot diverge by construction. To pass the check a human must confirm each capable pin is genuinely current frontier AND bump lastReviewedAt. An un-reviewed list fails loudly.",
|
|
3
3
|
"registrySchemaVersion": 2,
|
|
4
|
-
"$schemaNote": "registrySchemaVersion 2 = the enriched Doorway/Model Knowledge Registry.
|
|
4
|
+
"$schemaNote": "registrySchemaVersion 2 = the enriched Doorway/Model Knowledge Registry. Increment 1 (spec §Rollout step 1) added the per-door topModels[] frontier-derivation layer + the derived-frontier lint (report mode). Increment 2 (spec §Rollout step 2) ADDS the per-door probe{} block + candidateDoorways[] below — the metadata the deterministic prober (scripts/doorway-scan.mjs) reads to know HOW to re-verify each door — plus the live scan-state (.instar/state/doorway-scan.json) + the dark (enabled:false) doorway-scan job. Both additions are backward-compatible: the freshness lint ignores probe{}/candidateDoorways entirely (it reads only topModels/frontier/pins). GET /doorways is a LATER increment (spec §Rollout step 3) and is NOT wired yet.",
|
|
5
5
|
"lastReviewedAt": "2026-07-03",
|
|
6
6
|
"stalenessWindowDays": 45,
|
|
7
7
|
"enforcement": "report",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"name": "Claude Code CLI",
|
|
12
12
|
"accessMethod": "Claude Code CLI (subscription/OAuth)",
|
|
13
13
|
"status": "alive",
|
|
14
|
+
"probe": { "kind": "cli-version", "bin": "claude", "metered": false, "freeDefaultCovered": true },
|
|
14
15
|
"topModels": [
|
|
15
16
|
{ "id": "claude-opus-4-8", "role": "capable-anthropic", "frontier": true, "pricing": null, "verifiedAt": "carried-over-from-allowlist" },
|
|
16
17
|
{ "id": "claude-fable-5", "role": "ultra-anthropic", "frontier": true, "pricing": null, "verifiedAt": "carried-over-from-allowlist" }
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
"name": "Anthropic headless (claude -p)",
|
|
22
23
|
"accessMethod": "claude -p one-shot (Agent SDK credit pot)",
|
|
23
24
|
"status": "alive",
|
|
25
|
+
"probe": { "kind": "cli-version", "bin": "claude", "metered": false, "freeDefaultCovered": true },
|
|
24
26
|
"topModels": [
|
|
25
27
|
{ "id": "claude-opus-4-8", "role": "capable-anthropic", "frontier": true, "pricing": null, "verifiedAt": "carried-over-from-allowlist" },
|
|
26
28
|
{ "id": "claude-sonnet-4-6", "role": "balanced-anthropic", "frontier": true, "pricing": null, "verifiedAt": "carried-over-from-allowlist" },
|
|
@@ -32,6 +34,7 @@
|
|
|
32
34
|
"name": "Codex CLI",
|
|
33
35
|
"accessMethod": "codex exec (ChatGPT account)",
|
|
34
36
|
"status": "referenced-not-installed",
|
|
37
|
+
"probe": { "kind": "cli-version", "bin": "codex", "metered": false, "freeDefaultCovered": false },
|
|
35
38
|
"topModels": [
|
|
36
39
|
{ "id": "gpt-5.5", "role": "capable-openai", "frontier": true, "pricing": null, "verifiedAt": "carried-over-from-allowlist" }
|
|
37
40
|
],
|
|
@@ -41,6 +44,7 @@
|
|
|
41
44
|
"name": "Gemini CLI",
|
|
42
45
|
"accessMethod": "gemini -m <model> (Homebrew formula, OAuth)",
|
|
43
46
|
"status": "alive-but-deprecated-formula",
|
|
47
|
+
"probe": { "kind": "cli-version", "bin": "gemini", "metered": false, "freeDefaultCovered": true },
|
|
44
48
|
"topModels": [
|
|
45
49
|
{ "id": "gemini-3.1-pro-preview", "role": "capable-google", "frontier": true, "pricing": null, "verifiedAt": "carried-over-from-allowlist" }
|
|
46
50
|
],
|
|
@@ -50,6 +54,7 @@
|
|
|
50
54
|
"name": "pi CLI",
|
|
51
55
|
"accessMethod": "pi --model <provider>/<id> (multi-provider: openai-codex, openrouter, ...)",
|
|
52
56
|
"status": "alive",
|
|
57
|
+
"probe": { "kind": "cli-version", "bin": "pi", "metered": false, "vaultKeyName": "metered_openrouter_bench", "freeDefaultCovered": false },
|
|
53
58
|
"topModels": [],
|
|
54
59
|
"note": "Multi-provider consolidation door. Can reach OpenRouter (one door to many frontier models; vault key metered_openrouter_bench present). pi policy DENIES anthropic/claude via openrouter passthrough by default to protect subscription billing. No pin references this door yet, so topModels is empty (no frontier set to derive); the first enabled scan enumerates observed models for operator classification."
|
|
55
60
|
}
|
|
@@ -90,5 +95,15 @@
|
|
|
90
95
|
],
|
|
91
96
|
"$flaggedStaleNote": "Both prior flagged pins RESOLVED (operator-confirmed 2026-07-03): gemini capable swapped gemini-2.5-pro -> gemini-3.1-pro-preview (pin + topModels updated in the same change); codex capable stays gpt-5.5 (GA flagship — gpt-5.6-sol is preview-only/gov-gated/unreachable, NOT pinned). Nothing pending confirmation.",
|
|
92
97
|
"flaggedStale": [],
|
|
93
|
-
"$frontierAllowlistNote": "The hand-maintained frontierAllowlist{} was SUPERSEDED by derivation (spec §1.4): the frontier set for each door is now the ids in doors[door].topModels[] carrying frontier:true. This removes a second hand-maintained list (the rot this registry fights). The lint keeps a backward-compat path: a door with a literal frontierAllowlist[door] and NO topModels behaves exactly as before; a door with BOTH emits a transition finding so the old hand-list can't silently linger."
|
|
98
|
+
"$frontierAllowlistNote": "The hand-maintained frontierAllowlist{} was SUPERSEDED by derivation (spec §1.4): the frontier set for each door is now the ids in doors[door].topModels[] carrying frontier:true. This removes a second hand-maintained list (the rot this registry fights). The lint keeps a backward-compat path: a door with a literal frontierAllowlist[door] and NO topModels behaves exactly as before; a door with BOTH emits a transition finding so the old hand-list can't silently linger.",
|
|
99
|
+
"$candidateDoorwaysNote": "candidateDoorways[] = the doorways the scan should PROBE FOR (spec §1.2/§2.5), INCLUDING ones not yet present in doors{}. A candidate reachable but absent from doors is surfaced as a newly-configured-doorway. The prober only activates a candidate whose probe.kind it supports; a candidate with no door entry / unsupported kind is recorded, never silently treated as reachable. openrouter / gemini-direct-api below are candidate-only (no door entry yet) so the candidate-detection machinery has real inputs to exercise.",
|
|
100
|
+
"candidateDoorways": [
|
|
101
|
+
"claude-code",
|
|
102
|
+
"anthropic-headless",
|
|
103
|
+
"codex-cli",
|
|
104
|
+
"gemini-cli",
|
|
105
|
+
"pi-cli",
|
|
106
|
+
"openrouter",
|
|
107
|
+
"gemini-direct-api"
|
|
108
|
+
]
|
|
94
109
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-07-04T19:36:12.853Z",
|
|
5
|
+
"instarVersion": "1.3.758",
|
|
6
6
|
"entryCount": 202,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"domain": "identity",
|
|
12
12
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
13
13
|
"installedPath": ".instar/hooks/instar/session-start.sh",
|
|
14
|
-
"contentHash": "
|
|
14
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
15
15
|
"since": "2025-01-01"
|
|
16
16
|
},
|
|
17
17
|
"hook:dangerous-command-guard": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"domain": "safety",
|
|
21
21
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
22
22
|
"installedPath": ".instar/hooks/instar/dangerous-command-guard.sh",
|
|
23
|
-
"contentHash": "
|
|
23
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
24
24
|
"since": "2025-01-01"
|
|
25
25
|
},
|
|
26
26
|
"hook:grounding-before-messaging": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"domain": "safety",
|
|
30
30
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
31
31
|
"installedPath": ".instar/hooks/instar/grounding-before-messaging.sh",
|
|
32
|
-
"contentHash": "
|
|
32
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
33
33
|
"since": "2025-01-01"
|
|
34
34
|
},
|
|
35
35
|
"hook:compaction-recovery": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"domain": "identity",
|
|
39
39
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
40
40
|
"installedPath": ".instar/hooks/instar/compaction-recovery.sh",
|
|
41
|
-
"contentHash": "
|
|
41
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
42
42
|
"since": "2025-01-01"
|
|
43
43
|
},
|
|
44
44
|
"hook:external-operation-gate": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"domain": "safety",
|
|
48
48
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
49
49
|
"installedPath": ".instar/hooks/instar/external-operation-gate.js",
|
|
50
|
-
"contentHash": "
|
|
50
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
51
51
|
"since": "2025-01-01"
|
|
52
52
|
},
|
|
53
53
|
"hook:deferral-detector": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"domain": "safety",
|
|
57
57
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
58
58
|
"installedPath": ".instar/hooks/instar/deferral-detector.js",
|
|
59
|
-
"contentHash": "
|
|
59
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
60
60
|
"since": "2025-01-01"
|
|
61
61
|
},
|
|
62
62
|
"hook:self-stop-guard": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"domain": "coherence",
|
|
66
66
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
67
67
|
"installedPath": ".instar/hooks/instar/self-stop-guard.js",
|
|
68
|
-
"contentHash": "
|
|
68
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
69
69
|
"since": "2025-01-01"
|
|
70
70
|
},
|
|
71
71
|
"hook:post-action-reflection": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"domain": "evolution",
|
|
75
75
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
76
76
|
"installedPath": ".instar/hooks/instar/post-action-reflection.js",
|
|
77
|
-
"contentHash": "
|
|
77
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
78
78
|
"since": "2025-01-01"
|
|
79
79
|
},
|
|
80
80
|
"hook:external-communication-guard": {
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"domain": "safety",
|
|
84
84
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
85
85
|
"installedPath": ".instar/hooks/instar/external-communication-guard.js",
|
|
86
|
-
"contentHash": "
|
|
86
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
87
87
|
"since": "2025-01-01"
|
|
88
88
|
},
|
|
89
89
|
"hook:scope-coherence-collector": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"domain": "coherence",
|
|
93
93
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
94
94
|
"installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
|
|
95
|
-
"contentHash": "
|
|
95
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
96
96
|
"since": "2025-01-01"
|
|
97
97
|
},
|
|
98
98
|
"hook:scope-coherence-checkpoint": {
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"domain": "coherence",
|
|
102
102
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
103
103
|
"installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
|
|
104
|
-
"contentHash": "
|
|
104
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
105
105
|
"since": "2025-01-01"
|
|
106
106
|
},
|
|
107
107
|
"hook:free-text-guard": {
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"domain": "safety",
|
|
111
111
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
112
112
|
"installedPath": ".instar/hooks/instar/free-text-guard.sh",
|
|
113
|
-
"contentHash": "
|
|
113
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
114
114
|
"since": "2025-01-01"
|
|
115
115
|
},
|
|
116
116
|
"hook:claim-intercept": {
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"domain": "coherence",
|
|
120
120
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
121
121
|
"installedPath": ".instar/hooks/instar/claim-intercept.js",
|
|
122
|
-
"contentHash": "
|
|
122
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
123
123
|
"since": "2025-01-01"
|
|
124
124
|
},
|
|
125
125
|
"hook:claim-intercept-response": {
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"domain": "coherence",
|
|
129
129
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
130
130
|
"installedPath": ".instar/hooks/instar/claim-intercept-response.js",
|
|
131
|
-
"contentHash": "
|
|
131
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
132
132
|
"since": "2025-01-01"
|
|
133
133
|
},
|
|
134
134
|
"hook:stop-gate-router": {
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
"domain": "safety",
|
|
138
138
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
139
139
|
"installedPath": ".instar/hooks/instar/stop-gate-router.js",
|
|
140
|
-
"contentHash": "
|
|
140
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
141
141
|
"since": "2025-01-01"
|
|
142
142
|
},
|
|
143
143
|
"hook:auto-approve-permissions": {
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"domain": "safety",
|
|
147
147
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
148
148
|
"installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
|
|
149
|
-
"contentHash": "
|
|
149
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
150
150
|
"since": "2025-01-01"
|
|
151
151
|
},
|
|
152
152
|
"job:health-check": {
|
|
@@ -1562,7 +1562,7 @@
|
|
|
1562
1562
|
"type": "subsystem",
|
|
1563
1563
|
"domain": "updates",
|
|
1564
1564
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
1565
|
-
"contentHash": "
|
|
1565
|
+
"contentHash": "abb2e8ea9b3e906f37767153ca1ef7442cfc92ba27a50a7eaaa09596c8d5eede",
|
|
1566
1566
|
"since": "2025-01-01"
|
|
1567
1567
|
},
|
|
1568
1568
|
"subsystem:scheduler": {
|
|
@@ -1570,7 +1570,7 @@
|
|
|
1570
1570
|
"type": "subsystem",
|
|
1571
1571
|
"domain": "scheduling",
|
|
1572
1572
|
"sourcePath": "src/scheduler/JobScheduler.ts",
|
|
1573
|
-
"contentHash": "
|
|
1573
|
+
"contentHash": "0c92bf7ce40b2efdede00ee90f9c0bf8997c787691ae0054d6479ad96f28be67",
|
|
1574
1574
|
"since": "2025-01-01"
|
|
1575
1575
|
},
|
|
1576
1576
|
"subsystem:project-mapper": {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Doorway Scan
|
|
3
|
+
description: "Cadenced live re-probe of every doorway + top-model map refresh + drift diff. A deterministic prober (scripts/doorway-scan.mjs) probes CLIs (which/--version/model-list) and free model-list APIs, updates the per-agent live scan-state (.instar/state/doorway-scan.json), diffs against the previous scan, and raises ONE operator attention item with ONLY the changes (jargon-safe body + a private-view link to the raw diff). It NEVER auto-edits canonical source. Free probes by default; metered liveness / web-verify are opt-in, budget-capped, human-manual-only, and refuse on an unknown price. Ships OFF by default (enabled:false). perMachineIndependent — each machine scans its own disk. Spec: docs/specs/DOORWAY-MODEL-KNOWLEDGE-REGISTRY-SPEC.md §2."
|
|
4
|
+
schedule: "0 4 * * 1"
|
|
5
|
+
priority: low
|
|
6
|
+
expectedDurationMinutes: 10
|
|
7
|
+
model: haiku
|
|
8
|
+
supervision: tier1
|
|
9
|
+
enabled: false
|
|
10
|
+
perMachineIndependent: true
|
|
11
|
+
tags:
|
|
12
|
+
- cat:maintenance
|
|
13
|
+
- role:worker
|
|
14
|
+
- exec:prompt
|
|
15
|
+
gate: curl -sf http://localhost:${INSTAR_PORT:-4042}/health >/dev/null 2>&1
|
|
16
|
+
toolAllowlist: ["Bash"]
|
|
17
|
+
unrestrictedTools: false
|
|
18
|
+
mcpAccess: none
|
|
19
|
+
---
|
|
20
|
+
Run one doorway scan. This job only INVOKES the deterministic prober and sanity-checks that it produced a well-formed result — the prober owns 100% of the network I/O, secret handling, timeouts, sanitization, and delivery (it performs the POST /view + POST /attention itself, in-process). You have Bash ONLY — no Edit/Write tool — and a PreToolUse command-allowlist guard restricts this session's Bash to the sanctioned prober invocation + the /health gate + read-only plumbing. Do NOT try to edit any source file; this job NEVER auto-applies a change to the canonical registry (a stale map surfaces as a maintainer diff for the operator, never an edit).
|
|
21
|
+
|
|
22
|
+
1. **Prober-presence gate.** The prober is instar source — present only on a source-carrying agent (a maintainer/dev agent like Echo, and the fixture repos), NOT on a pure end-user agent. Run this SINGLE command (the command-allowlist guard permits only simple invocations — never chain it with `&&`/`||`/`;`):
|
|
23
|
+
`test -f scripts/doorway-scan.mjs`
|
|
24
|
+
If it exits NON-ZERO (the prober is absent), this agent carries no prober — EXIT SILENTLY, there is nothing to do (the honest no-op path every non-source agent takes). Do not message anyone. Only if it exits zero (present) do you proceed to step 2.
|
|
25
|
+
|
|
26
|
+
2. **Run the deterministic prober at the free scope (the ONLY scope the scheduled cadence runs).** This is a FIXED literal invocation — never substitute the scope, never append a metered scope; a metered scope spends money and is human-manual-only:
|
|
27
|
+
`node scripts/doorway-scan.mjs --scope free-probes`
|
|
28
|
+
The prober probes each configured door, updates the machine-local scan-state (`.instar/state/doorway-scan.json`), diffs against the last-surfaced baseline, and — only if there is a real, debounced, live-corroborated change — raises ONE attention item itself, in-process, with a machine-qualified `sourceContext` (`doorway-scan:<machineId>`) so two machines' findings never coalesce into one row. Free model-LIST probes spend ZERO tokens; the scheduled cadence spends no metered budget.
|
|
29
|
+
|
|
30
|
+
3. **Tier-1 supervision (your job) — sanity-check the run, do NOT re-surface anything yourself.** Confirm the prober exited cleanly and wrote/refreshed the scan-state. A crashed/partial run is NOT a signal — note it once and exit; the next cadence retries (the prober is fail-safe per-probe, and a completely-failed scan is handled by the prober's own breaker, which retries on a widening backoff and escalates ONE deduped item only after 3 consecutive complete failures). The prober already delivered any diff in-process — you do NOT build or POST an attention item, and you do NOT relay progress to Telegram.
|
|
31
|
+
|
|
32
|
+
4. Exit. This job produces a review artifact for the operator (a maintainer diff), not a running commentary. A clean scan is not news — surface nothing. The canonical map changes only when the operator folds a surfaced diff via instar-dev and re-runs the freshness lint; this job never touches source.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
The "Intelligence Infers, Keywords Only Guard" ratchet (`tests/unit/keyword-intent-decision-ratchet.test.ts`) was flipped from report mode to **enforcing** (`ENFORCE = false → true`) after a clean soak cycle — a net-new piece of code that decides what a human meant from a keyword/phrase list now **fails CI** until it is converted to LLM-with-context or justified as an allowlisted survivor. In the same change the three remaining latent offenders were resolved, dropping the baseline `4 → 1`:
|
|
9
|
+
|
|
10
|
+
- `src/core/TopicClassifier.ts` (`scoreKeywords`) — verified genuinely dead (zero runtime importers; only a test-only consumer) and **removed** (module + its e2e describe block).
|
|
11
|
+
- `src/core/AutonomySkill.ts` (`INTENT_PATTERNS`) — verified unwired (only a barrel re-export + its own unit test) and **removed** (module + barrel export in `src/index.ts` + unit test).
|
|
12
|
+
- `src/core/AgentReadinessScorer.ts` (`scoreText`) — a legitimate **survivor** (it scores a task's coordination-vs-judgment nature for the advisory `/agent-readiness` endpoint, not message intent) and was **allowlisted**, not removed.
|
|
13
|
+
|
|
14
|
+
Only `topicProfileIngress` remains as the sole baseline offender; its LLM conversion lands separately.
|
|
15
|
+
|
|
16
|
+
## What to Tell Your User
|
|
17
|
+
|
|
18
|
+
Nothing user-visible changed — this is internal development-infrastructure hardening. It makes a code-quality rule (never guess what a person meant from a keyword list — let the intelligence read the whole message) a hard build check instead of an advisory one, and deletes two unused internal modules that could have re-introduced that bug if wired up later. No feature, endpoint, or behavior you interact with is affected.
|
|
19
|
+
|
|
20
|
+
## Summary of New Capabilities
|
|
21
|
+
|
|
22
|
+
None — internal change (no user-facing surface). The keyword-intent ratchet now enforces (fails CI on a net-new offender) rather than only reporting, and two dead internal modules were removed.
|
|
23
|
+
|
|
24
|
+
## Evidence
|
|
25
|
+
|
|
26
|
+
- `npx vitest run tests/unit/keyword-intent-decision-ratchet.test.ts tests/e2e/discovery-round2-final.test.ts` → 17 passed; ratchet reports `[KEYWORD-INTENT] 1 keyword-list intent decisions (baseline 1, mode=ENFORCE)`.
|
|
27
|
+
- `npx tsc --noEmit` → exit 0 (no lingering references to the removed symbols).
|
|
28
|
+
- `npm run lint` → exit 0.
|
|
29
|
+
- Wiring verification recorded in the side-effects artifact: `grep` across `src/` for static imports, dynamic `import()`, `new X`, and function-name imports of the removed symbols → zero runtime consumers.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Doorway/Model Knowledge Registry — rollout increment 2 (the deterministic prober + live scan-state + the scan job, per `docs/specs/DOORWAY-MODEL-KNOWLEDGE-REGISTRY-SPEC.md` §Rollout step 2). **All of it ships DARK**: the scan job is `enabled:false` and no cadence runs until an operator enables it (rollout step 4). Nothing spends metered budget, mutates source, or changes model routing.
|
|
9
|
+
|
|
10
|
+
- **The deterministic prober** (`scripts/doorway-scan.mjs`, spec §2.0) — the security spine. It owns 100% of the network I/O, secret handling, timeouts, size-caps, sanitization, the scan-state read/write, the diff/debounce/breaker, and the in-process `POST /view` + `POST /attention` delivery; a tier-1 job session only invokes it. Secret-safe (vault key NAMES only; a value is used in an in-process request header, never in argv/environ/scan-state). Money is quadruple-gated: the scheduled cadence runs a fixed `--scope free-probes` (zero metered spend), and a metered scope is refused unless a manual-marker env AND a positive `budgetCapUsd` AND a known, fresh price are all present — plus a self-standing scheduled-session refusal that holds even if the §2.7 guard fails open.
|
|
11
|
+
- **Machine-local live scan-state** (`.instar/state/doorway-scan.json`, spec §1.3) — with an EXHAUSTIVE `loadScanState()` read-clamp that re-validates every field on every load (the on-disk plaintext file is treated as untrusted a local writer could poison after a clamped write), plus a coverage test so the clamp can't silently regrow holes.
|
|
12
|
+
- **Diff / debounce / breaker** (spec §2.3/§2.6) — an asymmetric 2-scan debounce (an alarming true→false flip needs corroboration; a recovery flips immediately), a `consecutiveCompleteFailures` breaker that retries on a widening backoff and escalates ONE deduped item only after 3 consecutive zero-door scans (P22 self-heal-before-notify), a tone-gate-safe jargon-safe attention body (raw diff in a private view; never a localhost/token-bearing link), and delivery robustness (the diff baseline advances only on a confirmed 2xx, with an initial-seed carve-out — no silent notice loss, no fresh-state flood).
|
|
13
|
+
- **The dark scan job template** (`src/scaffold/templates/jobs/instar/doorway-scan.md`, `enabled:false`, tier-1/haiku, `toolAllowlist:["Bash"]`) + the new **`perMachineIndependent` job-def flag** (spec §2.8/D11) threaded through `JobDefinition` → `PerSlugManifest` → `buildPerSlugManifest` → `InstallBuiltinJobs` → `manifestToJobDefinition`, and honored at `JobScheduler.triggerJob` (it skips the global jobSlug claim/lease so every machine scans its own disk).
|
|
14
|
+
- **The §2.7 PreToolUse command-allowlist guard** (`doorway-scan-guard.js`, installed via the always-overwrite `instar/` migration path) — a genuine stateful shell lexer (not a regex) that permits ONLY the sanctioned prober invocation + a host-pinned localhost curl + read-only `test -f`/`cat`/`jq -r`, refusing every write/exec/spend primitive. Scope resolution fails OPEN (never blocks an unrelated session); command matching fails CLOSED inside the doorway-scan session.
|
|
15
|
+
- **Manifest enrichment** (`scripts/model-registry-freshness.manifest.json`) — additively added the per-door `probe{}` block + `candidateDoorways[]` the prober reads to know HOW to re-verify each door. Backward-compatible: the freshness lint ignores both.
|
|
16
|
+
|
|
17
|
+
`GET /doorways`, the CLAUDE.md awareness block, and the `maintenance.doorwayScan` config-knob are increment 3; flipping the lint to `strict` + ratifying the standard is increment 5. Neither is in this increment.
|
|
18
|
+
|
|
19
|
+
## What to Tell Your User
|
|
20
|
+
|
|
21
|
+
Nothing changes for you right now — this increment ships **switched off**. It lands the machinery for a future opt-in weekly "doorway scan" that will quietly re-check which AI models each of your doorways (Claude Code, Codex, Gemini, a paid API key, …) can still reach, keep the map current, and send you ONE plain-English heads-up only when something actually changes. Until you deliberately turn the job on (a later step), it does no probing, spends no money, and sends no messages. There is nothing to do and nothing new to see.
|
|
22
|
+
|
|
23
|
+
## Summary of New Capabilities
|
|
24
|
+
|
|
25
|
+
- **(Dark / off by default)** A deterministic doorway prober + a per-machine live "what models can I reach?" scan-state, with an asymmetric debounce, a self-healing zero-doors breaker, and no-silent-loss delivery — none of it active until the operator enables the `doorway-scan` job.
|
|
26
|
+
- **A new `perMachineIndependent` job flag** so a per-machine-local job (like the doorway scan) runs on every machine independently instead of the lease electing just one.
|
|
27
|
+
- **A structural command-allowlist guard** that keeps the (future, opt-in) scan session from ever editing source or spending money — a genuine shell-parser allowlist that fails open outside that one session and fails closed inside it.
|
|
28
|
+
|
|
29
|
+
## Evidence
|
|
30
|
+
|
|
31
|
+
- `tests/unit/doorway-scan-prober.test.ts` (45) — clamp/sanitize, the exhaustive read-funnel + coverage, the P20/L5 probeStatus mapping, the asymmetric debounce (incl. flip-then-flip-back-clears), the diff, the breaker + backoff, the fail-closed money gate (D8/D9/D10 + pricing staleness + self-standing scheduled refusal), tone-gate-safe body + private-view XSS neutralization, the delivery-robustness planner, and the injected-dep network probes.
|
|
32
|
+
- `tests/unit/doorway-scan-guard.test.ts` (30) — drives the REAL deployed hook as a subprocess across the full §2.7 matrix: sanctioned shapes PERMITTED (incl. quoted-arg / collapsed-whitespace normalizations); cp/interpreters/perl/git-checkout/heredoc/compound/substitution/env-prefix/redirect/output-redirect/non-localhost-curl/metered-scope all REFUSED; scope fails OPEN; command matching fails CLOSED.
|
|
33
|
+
- `tests/unit/doorway-scan-job-template.test.ts` (11) — enabled:false, tier1/haiku, perMachineIndependent:true, weekday-pinned cron, Bash-only toolAllowlist, the fixed `--scope free-probes` literal, no `+liveness`/`+web-verify` string, the prober-presence gate, no data-carrying curl.
|
|
34
|
+
- `tests/unit/scheduler/per-machine-independent.test.ts` (2) — a job WITHOUT the flag still yields on `hasRemoteClaim===true`; a `perMachineIndependent` job never consults the claim path and proceeds.
|
|
35
|
+
- `tests/unit/instar-settings-hooks.test.ts` + `tests/unit/migration-parity-hooks.test.ts` — updated for the new guard entry (registered in settings + accepted migrator-only gap, matching the pr-hand-lease-guard precedent).
|
|
36
|
+
- `node scripts/lint-model-registry-freshness.mjs` → PASS on the enriched manifest; `npx tsc --noEmit` clean; full `pnpm lint` chain PASS; `pnpm build` OK (the new job hashes into the built-in-job lockfile). End-to-end: the real `doorway-scan.md` installs via `installBuiltinJobs` with zero errors; its per-slug manifest carries `enabled:false` + `perMachineIndependent:true`.
|
|
37
|
+
- Deferred items (spec §Deferred DF1-DF6) remain tracked in `docs/specs/reports/DOORWAY-MODEL-KNOWLEDGE-REGISTRY-followups.md` (filed by inc1).
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Side-Effects Review — Doorway/Model Knowledge Registry, increment 2 (deterministic prober + scan-state + diff/debounce/breaker + dark job + §2.7 guard)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `doorway-model-registry-inc2`
|
|
4
|
+
**Date:** `2026-07-04`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `recommended` — this increment adds a real BLOCK authority (the §2.7 PreToolUse command-allowlist guard hook, which can `exit 2` and block a Bash call) and touches the `JobScheduler` claim/lease seam. See §1/§2/§4/§5.
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Second rollout increment of `DOORWAY-MODEL-KNOWLEDGE-REGISTRY-SPEC.md` (§Rollout step 2), landed DARK: the doorway-scan job ships `enabled:false` and nothing runs on a cadence until an operator enables it (step 4). Artifacts:
|
|
11
|
+
|
|
12
|
+
1. **`scripts/doorway-scan.mjs`** — the deterministic prober (the security spine, §2.0). Owns all network I/O, secret handling, timeouts, size-caps, sanitization, scan-state read/write, diff/debounce/breaker, and the in-process `POST /view` + `POST /attention` delivery. Exports pure helpers for unit tests; `main()` runs only when invoked directly and is a harmless no-op stub in this dark increment.
|
|
13
|
+
2. **Machine-local scan-state schema** (`.instar/state/doorway-scan.json`, §1.3) — created lazily; an EXHAUSTIVE `loadScanState()` read-clamp re-validates every field on every load + a coverage test.
|
|
14
|
+
3. **Diff / debounce / breaker** (§2.3/§2.6) — asymmetric 2-scan debounce, a `consecutiveCompleteFailures` zero-doors breaker (retry-then-escalate, P22), tone-gate-safe attention body (raw diff → private view; never localhost/token link), and confirmed-delivery baseline advancement (no silent loss, no fresh-state flood).
|
|
15
|
+
4. **Dark job template** (`src/scaffold/templates/jobs/instar/doorway-scan.md`, `enabled:false`, tier1/haiku, `toolAllowlist:["Bash"]`) + the new **`perMachineIndependent` job-def flag** threaded `JobDefinition` → `PerSlugManifest` → `buildPerSlugManifest` → `InstallBuiltinJobs` → `manifestToJobDefinition`, honored at `JobScheduler.triggerJob` (skips the global jobSlug claim/lease).
|
|
16
|
+
5. **The §2.7 PreToolUse command-allowlist guard** (`doorway-scan-guard.js`) — installed via the always-overwrite `instar/` migration path + registered in `INSTAR_BASH_PRETOOLUSE_HOOKS`. A genuine stateful shell lexer allowing only the sanctioned prober invocation + a host-pinned localhost curl + read-only `test -f`/`cat`/`jq -r`.
|
|
17
|
+
6. **Manifest enrichment** — additive per-door `probe{}` + `candidateDoorways[]` (backward-compatible; the freshness lint ignores them).
|
|
18
|
+
|
|
19
|
+
## Decision-point inventory
|
|
20
|
+
|
|
21
|
+
- **`doorway-scan-guard.js` (NEW block authority)** — `add` — a PreToolUse Bash hook that can `exit 2` (block). SCOPE resolution fails OPEN; command matching fails CLOSED. Scoped by `INSTAR_JOB_SLUG==='doorway-scan'` (env-first, zero disk I/O off the hot path) → strict no-op in every other session.
|
|
22
|
+
- **`JobScheduler.triggerJob` claim/lease seam** — `modify` — a `perMachineIndependent` job now bypasses the claim check + claim-taking. Behavior for every job WITHOUT the flag is byte-identical (regression test).
|
|
23
|
+
- **The money gate (`meteredScopeGate`)** — `add` — a fail-closed predicate; refuses all metered spend on the scheduled/dark path. No metered completion is ever ISSUED in this increment (all test paths refuse before issuance).
|
|
24
|
+
- No message/dispatch/tone-gate decision point is touched.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 1. Over-block
|
|
29
|
+
|
|
30
|
+
The §2.7 guard is a block authority, so over-block is the primary risk. Mitigations:
|
|
31
|
+
- **Tightly scoped:** the guard is an immediate no-op unless `INSTAR_JOB_SLUG==='doorway-scan'` (a scheduler-set env only the dark doorway-scan job carries). An instar-dev session, an interactive session, and every other job session hit the env-first fast path and ALLOW with zero disk I/O — proven by the subprocess test (a dangerous `cp` under another slug / no slug / a non-Bash tool all return exit 0).
|
|
32
|
+
- **Fails OPEN on scope:** any error resolving whether this is the doorway-scan session → ALLOW (exactly like the sibling `pr-hand-lease-guard.js`). A guard bug can never lock out an unrelated session's Bash. Proven (malformed stdin → exit 0).
|
|
33
|
+
- **Inside the doorway-scan session, over-block is intentional and bounded:** the guard fails CLOSED, but that session's only legitimate commands are the sanctioned shapes (the job body issues exactly `test -f …` and `node scripts/doorway-scan.mjs --scope free-probes`). A false-refuse there breaks the DARK scan loudly/safely, never widens scope — and the job is off by default, so the blast radius until step 4 is nil.
|
|
34
|
+
- The `enabled:false` job means the guard is never even reached on any agent until an operator opts in.
|
|
35
|
+
|
|
36
|
+
## 2. Under-block
|
|
37
|
+
|
|
38
|
+
- **Guard under-block (letting a source write through):** the matcher is an ALLOWLIST with a genuine lexer — anything not provably ONE sanctioned simple command is REFUSED, so obfuscation (compounds, substitution, redirects, env-prefix, interpreters, heredocs, `curl -o`, non-localhost curl) is refused by construction, not by a denylist that can miss an idiom. The adversarial subprocess matrix asserts every such primitive REFUSED.
|
|
39
|
+
- **Money under-gate (a scheduled run spending):** quadruple-gated + a self-standing scheduled-session refusal that holds even if the guard fails open. `meteredScopeGate` refuses on free-scope / scheduled-session / no-marker / absent-or-nonpositive-budget / unknown-or-stale-price / over-cap — tested on every branch. No completion is issued on any refused path.
|
|
40
|
+
- **Scheduler under-block:** `perMachineIndependent` deliberately bypasses the claim — that is the intended behavior for a per-machine-idempotent, cheap, local-only scan (the misuse warning on the flag documents the boundary). No shared/canonical state is mutated on the cadence.
|
|
41
|
+
|
|
42
|
+
## 3. Level-of-abstraction fit
|
|
43
|
+
|
|
44
|
+
Correct layers. The prober is a deterministic script (a detector that produces a signal + a maintainer diff), never a runtime authority over routing (canonical is authoritative — §1.1). The guard is a structural PreToolUse enforcer (Structure > Willpower) that replaces a prompt-level "don't edit source" wish. The `perMachineIndependent` flag lives on `JobDefinition` alongside its sibling `writesState`/`resumeOnReap` opt-in flags — the honest place for a per-job scheduling property.
|
|
45
|
+
|
|
46
|
+
## 4. Signal vs authority compliance
|
|
47
|
+
|
|
48
|
+
**Required reference:** `docs/signal-vs-authority.md`
|
|
49
|
+
|
|
50
|
+
- The **prober + diff/breaker** are SIGNAL: they surface a maintainer diff / a deduped escalation to the operator; they never auto-apply to source (enforced structurally by the guard + `toolAllowlist`), never gate a message.
|
|
51
|
+
- The **§2.7 guard** is an AUTHORITY, but its veto rests on an OBJECTIVE, decidable shape check (a genuine shell-grammar parse to a single sanctioned simple-command), not brittle inference; it fails OPEN on scope (never blocks the wrong session) and CLOSED on match (a refused command breaks the dark scan safely). This is the same shape as the sibling `pr-hand-lease-guard.js` and the eli16/release-fragment gates: block carried by an objective binary, escape hatch is "run a sanctioned command."
|
|
52
|
+
- The **money gate** is an authority over spend, and it is fail-closed by construction (no spend without an explicit manual marker + positive cap + known/fresh price + non-scheduled session).
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 5. Interactions
|
|
57
|
+
|
|
58
|
+
- **Shadowing:** the guard is one more PreToolUse Bash entry appended to `INSTAR_BASH_PRETOOLUSE_HOOKS`; it does not replace or reorder the existing guards, and it is a no-op outside the doorway-scan slug, so it never shadows another guard's decision.
|
|
59
|
+
- **Double-fire:** none. The prober is the sole in-process POST actor; the job session runs no data-carrying curl. The scan job is off; when enabled it is `perMachineIndependent` (one run per machine, deliberately — never a duplicated cross-machine side-effect, since it only reads local disk + writes local scan-state).
|
|
60
|
+
- **Races:** the scan-state is a single machine-local file written atomically (temp + rename); no cross-process shared mutable state. `perMachineIndependent` removes the claim path entirely, so there is no claim race for this job.
|
|
61
|
+
- **Feedback loops:** none. The prober never edits source; the diff is a review artifact. The breaker retries on a widening backoff (never a tight loop) and escalates once (deduped).
|
|
62
|
+
|
|
63
|
+
## 6. External surfaces
|
|
64
|
+
|
|
65
|
+
- **Other agents on the machine:** the guard fires fleet-wide on every Bash call but is a strict env-first no-op for all non-doorway-scan sessions (zero disk I/O on that path).
|
|
66
|
+
- **Install base:** the job template installs via `InstallBuiltinJobs` (non-destructive install-if-missing, on init + update). The guard hook installs via the always-overwrite `instar/` `migrateHooks()` path + `ensureInstarBashPreToolUseHooks` settings wiring — migrator-only for now (documented gap, matching pr-hand-lease-guard: fires only in the dark job's session, so inert until enabled; a fresh-init agent gets it on first auto-update, well before any operator enables the job). The prober + manifest enrichment ship as source (present on source-carrying agents; absent on pure end-user agents → the prober-presence gate makes the job a silent no-op).
|
|
67
|
+
- **External systems (Telegram/Slack/GitHub/Cloudflare):** none in this dark increment (the prober's `POST /attention`/`POST /view` are local + only reached when the job is enabled).
|
|
68
|
+
- **Persistent state:** `.instar/state/doorway-scan.json` (machine-local, created lazily on first scan, EXCLUDED from the backup manifest by design — never replicated onto another machine). No writes happen until the job is enabled.
|
|
69
|
+
- **Operator surface (Mobile-Complete Operator Actions):** none added in this increment (the `GET /doorways` read surface + CLAUDE.md awareness are increment 3). Not applicable.
|
|
70
|
+
|
|
71
|
+
## 6b. Operator-surface quality
|
|
72
|
+
|
|
73
|
+
No operator-facing surface added — not applicable. (When the job is enabled in step 4, its only operator output is a jargon-safe attention item + a private view — both already designed tone-gate-safe.)
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
78
|
+
|
|
79
|
+
- The **prober + manifest + guard + job template** are git-tracked instar source / built-in templates → byte-identical on every machine by construction. No divergence, no replication path.
|
|
80
|
+
- The **live scan-state** is machine-local BY DESIGN (a door's liveness is a physical fact of one disk — the identical posture of the Playwright Profile Registry). It is deliberately EXCLUDED from `BackupManager.includeFiles` — restoring one machine's disk-liveness onto another would be actively false. A `perMachineIndependent` job is the structural expression: each machine runs its own scan and writes its own scan-state; no lease elects a single machine.
|
|
81
|
+
- **One-voice / notices:** when enabled, each machine's diff uses a machine-qualified `sourceContext` (`doorway-scan:<machineId>`) so two machines' findings stay DISTINCT rows (never coalesced), and route to the alerts/hub topic (P23) — not a per-machine forum topic.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 8. Rollback cost
|
|
86
|
+
|
|
87
|
+
DARK increment — **revert-and-patch, near-zero blast radius.**
|
|
88
|
+
- Hot-fix: `git revert` the PR. The guard hook is removed on the next migration (always-overwrite path re-writes the hook set; a settings entry pointing at a now-absent hook is a non-blocking error → Bash proceeds). The job template is retired by `InstallBuiltinJobs` when the template disappears (manifest flipped `enabled:false`+retired). The `perMachineIndependent` reverts cleanly (the flag simply disappears; jobs fall back to the claim path). The manifest `probe{}`/`candidateDoorways` are additive and the lint ignores them, so removing them is inert.
|
|
89
|
+
- Data migration: none — no scan-state is written while the job is off; a stale scan-state file is regenerable and self-healing (corrupt/old-schema → fresh, additive backfill).
|
|
90
|
+
- Agent state repair: none.
|
|
91
|
+
- User visibility: none — everything is off by default.
|
|
92
|
+
- Kill switch short of revert: the job stays `enabled:false`; the guard is inert outside the (disabled) doorway-scan session.
|
|
93
|
+
|
|
94
|
+
## Second-pass review
|
|
95
|
+
|
|
96
|
+
Concur with the review. I independently read the guard hook string in `getDoorwayScanGuardHook()`, the `JobScheduler.triggerJob` staged diff, the `meteredScopeGate`/`main()`/lexer in `scripts/doorway-scan.mjs`, and all three test files. The fail-OPEN-on-scope / fail-CLOSED-on-match claim is TRUE in the code: Region A returns `exit 0` for a non-Bash tool, a non-string command, any JSON-parse error, and — the load-bearing check — any session where `INSTAR_JOB_SLUG !== 'doorway-scan'` (plus an 8s fail-open stdin backstop), so an instar-dev/interactive/other-job session can never be over-blocked; Region B (only reachable inside the doorway-scan session) blocks with `exit 2` on any non-sanctioned command AND on a thrown lexer error. `lexSimpleCommand` is a genuine stateful char-by-char tokenizer (single/double-quote spans, backslash escapes, quote-state tracking — not a substring/regex scan) that refuses every operator/redirection/expansion/substitution/newline and the leading `NAME=` env-prefix; I could not find a source-write or metered-spend primitive that slips through (`node` is admitted only for the exact prober argv, so `node -e` is refused; curl is host-pinned to localhost with output-redirect and non-allowlisted flags refused), and the adversarial subprocess matrix drives the real deployed hook and asserts both directions. The `perMachineIndependent` bypass leaves the no-flag path byte-identical (the flag is falsy for normal jobs, so `claimPath`, `remoteHeld`, and the claim-taking `if/else-if/else` all reduce to the original expressions; the regression test confirms a no-flag job still yields on a remote claim while the pmi job never consults the claim path). The money gate is genuinely fail-closed: the self-standing scheduled-session refusal precedes the marker check, both defaults (`manualMarker=false`, `isScheduledSession=false`) still refuse, `main()` issues no completion and forces `free-probes` on the scheduled/dark path, and every refusal branch is unit-tested. (Note: `grep` for the literal `meteredScopeGate` intermittently returned nothing in this environment, but Read + `sed` both show the full branch-by-branch test suite at lines 223-254 — a tooling quirk, not a missing test.)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Side-Effects Review — Keyword-Intent Ratchet: flip to ENFORCE + resolve 3 latent offenders
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `keyword-lint-enforce`
|
|
4
|
+
**Date:** `2026-07-04`
|
|
5
|
+
**Author:** `Echo (instar-dev agent)`
|
|
6
|
+
**Second-pass reviewer:** `fork reviewer (see below)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Gives the "Intelligence Infers, Keywords Only Guard" ratchet (`tests/unit/keyword-intent-decision-ratchet.test.ts`) its enforcement teeth and clears the last three latent offenders. Two edits carry the weight: `ENFORCE = false → true` (the `<= BASELINE` guard is now a hard CI failure, not a warning) and `BASELINE = 4 → 1`. The three original latent offenders are resolved: `core/TopicClassifier.ts` and `core/AutonomySkill.ts` are **removed** as genuinely-dead code (verified zero runtime importers), and `core/AgentReadinessScorer.ts` is **allowlisted** as a legitimate task-nature survivor (it scores a task's coordination-vs-judgment ratio for an advisory endpoint, not message intent). Supporting edits: drop the `AutonomySkill` barrel export from `src/index.ts`, delete `tests/unit/AutonomySkill.test.ts`, surgically remove the `TopicClassifier` describe block + imports from `tests/e2e/discovery-round2-final.test.ts`, and drop the two deleted class names from the `under-the-hood.md` class list. Docs (`spec` + `eli16`) extended to record the flip. The only decision-point surface here is a CI-time lint ratchet; there is no runtime message-path behavior change.
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `keyword-intent-decision-ratchet` CI ratchet — **modify** — flips from report-mode (warn) to enforcing (fail CI on net-new offender); baseline lowered 4→1.
|
|
15
|
+
- `TopicClassifier.scoreKeywords` (runtime) — **remove** — dead-code keyword classifier, no runtime callers; deleted.
|
|
16
|
+
- `AutonomySkill.INTENT_PATTERNS` (runtime) — **remove** — unwired keyword intent recognizer; deleted.
|
|
17
|
+
- `AgentReadinessScorer.scoreText` (runtime) — **pass-through** — unchanged; only its ratchet classification moves (offender → allowlisted survivor).
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. Over-block
|
|
22
|
+
|
|
23
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
24
|
+
|
|
25
|
+
The only "block" surface is CI. Over-block here = the ratchet failing a PR that is not actually a keyword-intent offender (a false positive). This is the risk the enforcement flip introduces. It is mitigated structurally: the detector is deliberately tuned to under-report (errs toward false negatives), the audit's cleared classes are explicitly allowlisted, and the change ran a full soak cycle in report mode (merged 2026-07-03, many PRs since) with zero false positives. Verified on this branch: the ratchet flags exactly one file (`topicProfileIngress`), matching `BASELINE = 1`. There is no runtime/message over-block surface — this never rejects a user message.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 2. Under-block
|
|
30
|
+
|
|
31
|
+
**What failure modes does this still miss?**
|
|
32
|
+
|
|
33
|
+
The detector's two regex signatures can miss a subtly-shaped keyword-intent gate (e.g. a list assembled at runtime, or a message-var name outside the recognized set). This is the intended conservatism (a noisy ratchet gets disabled). A NEW offender added *inside* an allowlisted file is also masked — including, now, `AgentReadinessScorer.ts`: a future keyword-intent message gate added to that file would not be caught. This is the documented, accepted cost of every allowlist entry. The removal of the two dead modules strictly shrinks the under-block surface (two fewer places a keyword classifier could be silently re-wired).
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 3. Level-of-abstraction fit
|
|
38
|
+
|
|
39
|
+
**Is this at the right layer?**
|
|
40
|
+
|
|
41
|
+
Yes. The ratchet is a build-time lint (a low-cost structural guard), sibling to `no-silent-fallbacks`, enforcing a constitutional standard at the exact layer that standard is meant to bite — before merge, in CI, per-file. It is not a runtime authority over messages (that would be the wrong layer for a keyword-shaped detector). The flip to enforce is the planned graduated-rollout step the original spec's Rollout section already specified. The dead-code removals are at the correct layer too: deleting an unused module is the right resolution for a latent offender that nothing wires — converting it to LLM-with-context would be dead work on dead code.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 4. Signal vs authority compliance
|
|
46
|
+
|
|
47
|
+
**Required reference:** docs/signal-vs-authority.md
|
|
48
|
+
|
|
49
|
+
- [x] Yes — but the logic is appropriate for its layer: a CI-time ratchet whose "authority" is failing a build on a per-file structural count, with a human-authored allowlist + explicit baseline doing the precision work. It holds no runtime authority over any message.
|
|
50
|
+
|
|
51
|
+
The ratchet is a deterministic structural check, not a runtime gate over conversational content. Its "block" is a red CI check that a human (or the agent) resolves by converting the offender to LLM-with-context or justifying it as a survivor — exactly the graduated no-silent-fallbacks pattern. It never sits in the message path. Brittle-logic-with-runtime-block-authority (the anti-pattern this question guards) does not apply.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 5. Interactions
|
|
56
|
+
|
|
57
|
+
- **Shadowing:** none. The ratchet is a standalone vitest file; it does not run before/after another check in any pipeline. Removing the two modules cannot shadow anything — nothing imported them.
|
|
58
|
+
- **Double-fire:** none. Single test file, single assertion path.
|
|
59
|
+
- **Races:** none. No shared mutable state; the detector reads source files at test time.
|
|
60
|
+
- **Feedback loops:** none.
|
|
61
|
+
- **Test-file interaction:** `tests/e2e/discovery-round2-final.test.ts` imported `TopicClassifier`; its describe block was surgically removed and the remaining three blocks (which test `FeatureRegistry`, not `TopicClassifier`) verified green (12 tests pass). `tests/unit/AutonomySkill.test.ts` was the only consumer of `AutonomySkill` and was deleted with it.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 6. External surfaces
|
|
66
|
+
|
|
67
|
+
- Other agents / install base: no runtime change — this is a test + dead-code removal. Removing the `AutonomySkill` barrel export from `src/index.ts` is technically a public-API narrowing, but instar is consumed as a CLI/server, not as a library whose consumers import `AutonomySkill`; the audit itself classified it "exported, unwired". No known consumer.
|
|
68
|
+
- External systems (Telegram/Slack/GitHub/Cloudflare): none.
|
|
69
|
+
- Persistent state: none — no ledgers, DBs, or memory files touched.
|
|
70
|
+
- Timing/runtime: none.
|
|
71
|
+
- **Operator surface:** no operator-facing actions added or touched — not applicable.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 6b. Operator-surface quality
|
|
76
|
+
|
|
77
|
+
No operator surface — not applicable. This change touches only a test, two deleted `src/core` modules, a barrel export, and docs; no dashboard renderer, approval page, or grant/secret form.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
82
|
+
|
|
83
|
+
**Machine-local BY DESIGN** — with the reason: this is a build-time CI lint plus source-tree deletions. It has no runtime state, emits no user-facing notices, holds no durable state that could strand on topic transfer, and generates no URLs. The ratchet runs identically in every checkout because it reads the committed source tree; there is nothing to replicate or proxy. A multi-machine agent sees byte-identical behavior on every machine.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 8. Rollback cost
|
|
88
|
+
|
|
89
|
+
**Pure code + test change — revert and ship a patch.** No persistent state, no data migration, no agent-state repair, no user-visible regression during the rollback window. The narrow rollback lever for just the enforcement teeth is a one-line flip (`ENFORCE = true → false`) back to report mode. Restoring the two deleted modules, if ever genuinely wanted, is a `git revert` — their full source lives in history. `BASELINE` and `EXPECTED_OFFENDERS` are kept internally consistent, so a revert is mechanical.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Conclusion
|
|
94
|
+
|
|
95
|
+
The review produced no design changes. The change is proportionate: it flips a lint to enforcing after a clean soak (the planned rollout step) and resolves the three latent offenders the correct way per offender — delete the two verified-dead keyword classifiers, allowlist the one genuine task-nature survivor. The only genuine risk (a CI false positive from the enforcement flip) is mitigated by the soak, the conservatism-first detector, and the allowlist, and is confirmed absent on this branch (offenders == baseline == 1). tsc, lint, the ratchet suite, and the affected e2e all green. Clear to ship.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Second-pass review (if required)
|
|
100
|
+
|
|
101
|
+
**Reviewer:** independent reviewer subagent (Phase 5 — change touches a "guard"/"gate")
|
|
102
|
+
**Independent read of the artifact: concur**
|
|
103
|
+
|
|
104
|
+
Concur with the review. Independently verified: the diff matches the artifact (deletions of `AutonomySkill.ts`/`TopicClassifier.ts`/their tests, ratchet `ENFORCE false→true` + `BASELINE 4→1`, `EXPECTED_OFFENDERS == ['core/topicProfileIngress.ts']`, `AgentReadinessScorer` moved to ALLOWLIST); the two removed modules are truly dead (grep across `src/` → only a comment reference, zero runtime consumers); `AgentReadinessScorer` is genuinely wired at `src/server/routes.ts` for the `/agent-readiness` endpoint and scores task-nature (`COORDINATION_SIGNALS`/`JUDGMENT_SIGNALS` density → `coordinationRatio`), not message intent — the allowlist justification is accurate; `tsc --noEmit` exit 0; `vitest` 17 passed (ratchet in ENFORCE, baseline 1); the ratchet's internal guards remain coherent.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Evidence pointers
|
|
109
|
+
|
|
110
|
+
- `npx vitest run tests/unit/keyword-intent-decision-ratchet.test.ts tests/e2e/discovery-round2-final.test.ts` → 17 passed; ratchet prints `[KEYWORD-INTENT] 1 keyword-list intent decisions (baseline 1, mode=ENFORCE)`.
|
|
111
|
+
- `npx tsc --noEmit` → exit 0 (no lingering references to removed symbols).
|
|
112
|
+
- `npm run lint` → exit 0.
|
|
113
|
+
- Wiring verification: `grep` across `src/` for static imports, dynamic `import()`, `new X`, and function-name imports of `TopicClassifier`/`AutonomySkill`/`classifyForDiscovery` → zero runtime consumers.
|