software-defence-factory 0.7.0 → 0.8.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/bin/software-defence-factory.mjs +43 -6
- package/docs/interfaces.md +1 -1
- package/docs/quickstart.md +121 -6
- package/docs/recovery.md +184 -4
- package/docs/setup.md +70 -0
- package/factory/delivery-provider.mjs +20 -0
- package/factory/delivery.mjs +881 -0
- package/factory/execution-evidence.mjs +7 -0
- package/factory/execution-profile.mjs +11 -0
- package/factory/executor.mjs +100 -44
- package/factory/inference-redaction.mjs +123 -0
- package/factory/lib.mjs +35 -6
- package/factory/model-environment.mjs +166 -0
- package/factory/processes.mjs +47 -14
- package/factory/providers/github-delivery.mjs +161 -0
- package/factory/queue.mjs +12 -1
- package/factory/scratch.mjs +15 -0
- package/factory/server.mjs +15 -3
- package/factory/source-admission.mjs +6 -0
- package/factory/ui/assets/index-B13yiAWU.css +1 -0
- package/factory/ui/assets/index-BuZQnEyv.js +13 -0
- package/factory/ui/index.html +2 -2
- package/factory/workflow-qualification.mjs +541 -0
- package/operator-skills/factory-foundation/SKILL.md +5 -1
- package/package.json +1 -1
- package/scripts/probe-platform.mjs +34 -5
- package/factory/ui/assets/index-Bz6ECdy_.css +0 -1
- package/factory/ui/assets/index-DqeOnrGK.js +0 -13
|
@@ -4,7 +4,7 @@ import { resolve, join } from 'node:path';
|
|
|
4
4
|
import { randomBytes } from 'node:crypto';
|
|
5
5
|
import { spawn } from 'node:child_process';
|
|
6
6
|
import { createServer } from 'node:net';
|
|
7
|
-
import { ROOT, PINS, DEFAULT_STATE, configAt, save, json, run, stream, digest, api, sleep, stopContainers } from '../factory/lib.mjs';
|
|
7
|
+
import { ROOT, PINS, DEFAULT_STATE, configAt, save, json, run, stream, digest, api, sleep, stopContainers, PUBLICATION_API_TIMEOUT_MS } from '../factory/lib.mjs';
|
|
8
8
|
import { assertInstalledJobImage, installCustomJobImage, installStandardJobImage, inspectImageInstallation } from '../factory/image-install.mjs';
|
|
9
9
|
import { listIssues, readIssue } from '../factory/issue-intake.mjs';
|
|
10
10
|
import { recommendWork } from '../factory/intake.mjs';
|
|
@@ -34,7 +34,7 @@ let state = resolve(flags.state || DEFAULT_STATE);
|
|
|
34
34
|
if(existsSync(state))state=realpathSync(state);
|
|
35
35
|
const alive = pid => { try { process.kill(pid,0); return true; } catch(error) { if(error.code === 'ESRCH')return false; throw error; } };
|
|
36
36
|
|
|
37
|
-
function init(repo, harness='codex', check='', port=7331, sourceRef='HEAD') {
|
|
37
|
+
function init(repo, harness='codex', check='', port=7331, sourceRef='HEAD', delivery, inferenceProvider) {
|
|
38
38
|
repo=realpathSync(resolve(repo));
|
|
39
39
|
if (existsSync(join(state,'factory.json'))) throw new Error('Already configured; edit the private factory.json explicitly or choose another --state');
|
|
40
40
|
if ([repo,state,ROOT].some(p=>/[,\n\r]/.test(p))) throw new Error('Paths cannot contain commas or line breaks');
|
|
@@ -45,11 +45,11 @@ function init(repo, harness='codex', check='', port=7331, sourceRef='HEAD') {
|
|
|
45
45
|
if (!argv) throw new Error('Select codex, pi, mock or custom with --command-json');
|
|
46
46
|
if (flags.model && ['codex','pi'].includes(harness)) argv.splice(harness==='codex'?argv.length-1:argv.length,0,'--model',flags.model);
|
|
47
47
|
mkdirSync(state,{recursive:true,mode:0o700});state=realpathSync(state);chmodSync(state,0o700);
|
|
48
|
-
save(join(state,'factory.json'),{version:1,repo,sourceRef,harness,command:argv,check,port:Number(port),image:PINS.jobImage,network:harness==='mock'?'none':'bridge',timeoutSeconds:1800,memoryMiB:2048,model:flags.model || null,
|
|
48
|
+
save(join(state,'factory.json'),{version:1,repo,sourceRef,harness,command:argv,check,port:Number(port),image:PINS.jobImage,network:harness==='mock'?'none':'bridge',timeoutSeconds:1800,memoryMiB:2048,model:flags.model || null,...(inferenceProvider?{inferenceProvider}:{}),...(delivery?{delivery}:{}),
|
|
49
49
|
scope:{project:'pilot',service:'app',environment:'test',owner:'operator'}});
|
|
50
50
|
configAt(state);
|
|
51
51
|
writeFileSync(join(state,'worker.token'),randomBytes(32).toString('hex')+'\n',{mode:0o600});
|
|
52
|
-
writeFileSync(join(state,'model.env'),'#
|
|
52
|
+
writeFileSync(join(state,'model.env'),'# Supported inference settings only. Never add host, forge, deploy, Docker or cloud identity credentials.\n',{mode:0o600});
|
|
53
53
|
registerInstallation(state);
|
|
54
54
|
console.log(`Configured ${state}\nApp files were not changed. Only committed code is cloned into jobs.`);
|
|
55
55
|
}
|
|
@@ -124,8 +124,39 @@ async function jobAction(action) {
|
|
|
124
124
|
console.log(`${action}: ${id}`);
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
async function publishJob(jobId) {
|
|
128
|
+
if (!/^job_[a-f0-9]{24}$/.test(jobId || '')) throw new Error('publish requires a Factory JOB_ID');
|
|
129
|
+
const snapshot=await api(state,'/api/v1/status'),job=snapshot.jobs.find(item=>item.id===jobId);
|
|
130
|
+
if(!job)throw new Error('Job not found');
|
|
131
|
+
const current=job.runs.at(-1);
|
|
132
|
+
const receipt=await api(state,`/api/v1/jobs/${jobId}/publish`,{run_id:current?.id},undefined,{timeoutMs:PUBLICATION_API_TIMEOUT_MS});
|
|
133
|
+
console.log(JSON.stringify(receipt,null,2));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function abandonDeliveryJob(jobId) {
|
|
137
|
+
if (!/^job_[a-f0-9]{24}$/.test(jobId || '')) throw new Error('abandon-delivery requires a Factory JOB_ID');
|
|
138
|
+
const branchSha = flags['branch-sha'];
|
|
139
|
+
if (!/^[a-f0-9]{40}$/.test(branchSha || '')) throw new Error('abandon-delivery requires --branch-sha with the inspected remote SHA');
|
|
140
|
+
const snapshot=await api(state,'/api/v1/status'),job=snapshot.jobs.find(item=>item.id===jobId);
|
|
141
|
+
if(!job)throw new Error('Job not found');
|
|
142
|
+
const delivery=job.delivery_status;
|
|
143
|
+
if(!delivery?.can_abandon)throw new Error('This job has no resolvable pre-write branch collision; inspect status and reconcile unresolved provider effects.');
|
|
144
|
+
if(branchSha!==delivery.remote_collision?.sha)throw new Error('The supplied branch SHA differs from current status; inspect the current remote branch before resolving.');
|
|
145
|
+
const result=await api(state,`/api/v1/jobs/${jobId}/abandon-delivery`,{
|
|
146
|
+
run_id:job.runs.at(-1)?.id,delivery_identity:delivery.identity,branch_sha:branchSha,
|
|
147
|
+
},undefined,{timeoutMs:PUBLICATION_API_TIMEOUT_MS});
|
|
148
|
+
console.log(JSON.stringify(result,null,2));
|
|
149
|
+
}
|
|
150
|
+
|
|
127
151
|
try {
|
|
128
|
-
if(command==='init') {
|
|
152
|
+
if(command==='init') {
|
|
153
|
+
if(!flags.repo)throw new Error('init requires --repo /path/to/existing/git/repo');
|
|
154
|
+
if(flags.harness && flags.agent && flags.harness !== flags.agent)throw new Error('--harness conflicts with legacy --agent');
|
|
155
|
+
const deliveryFlags=[flags['delivery-provider'],flags['delivery-repository'],flags['delivery-target']];
|
|
156
|
+
if(deliveryFlags.some(Boolean)&&deliveryFlags.some(value=>!value))throw new Error('Trusted PR delivery requires --delivery-provider github --delivery-repository URL --delivery-target main|dev');
|
|
157
|
+
const delivery=deliveryFlags.every(Boolean)?{provider:flags['delivery-provider'],repository:flags['delivery-repository'],target:flags['delivery-target']}:undefined;
|
|
158
|
+
init(flags.repo,flags.harness || flags.agent,flags.check,flags.port,flags['source-ref'] || 'HEAD',delivery,flags['inference-provider']);
|
|
159
|
+
}
|
|
129
160
|
else if(command==='install')await withServiceOperation('install',install);
|
|
130
161
|
else if(command==='up') { if(hasService(state))await manageService('controller','start',state);else await withServiceOperation('up',up); }
|
|
131
162
|
else if(command==='stop') { if(hasService(state))await manageService('controller','stop',state);else await withServiceOperation('stop',stop); }
|
|
@@ -222,6 +253,8 @@ try {
|
|
|
222
253
|
if(!flags.file)throw new Error('Use --file incident.json; see factory/examples/incident.json');
|
|
223
254
|
console.log(JSON.stringify(await admitIncident(state,json(resolve(flags.file)),submit)));
|
|
224
255
|
} else if(['approve','cancel','retry'].includes(command))await jobAction(command);
|
|
256
|
+
else if(command==='publish')await publishJob(positional[0]);
|
|
257
|
+
else if(command==='abandon-delivery')await abandonDeliveryJob(positional[0]);
|
|
225
258
|
else if(command==='revise')await jobAction('request_changes');
|
|
226
259
|
else if(command==='demo') {
|
|
227
260
|
state=resolve(flags.state || DEFAULT_DEMO_STATE);
|
|
@@ -244,6 +277,8 @@ try {
|
|
|
244
277
|
demo Install and run a synthetic sample (no model key)
|
|
245
278
|
qualify --state PATH Exercise recovery and isolation with a stopped demo job
|
|
246
279
|
init --repo PATH --harness codex|pi|custom --check "npm ci && npm test" [--source-ref REF]
|
|
280
|
+
[--inference-provider PROVIDER]
|
|
281
|
+
[--delivery-provider github --delivery-repository https://github.com/OWNER/REPO --delivery-target main|dev]
|
|
247
282
|
install [--image LOCAL_REF] Build the standard image, or select an existing local image
|
|
248
283
|
doctor | up | status | stop Inspect / operate your private installation
|
|
249
284
|
foundation Read the operator setup skill; no installation required
|
|
@@ -280,6 +315,8 @@ try {
|
|
|
280
315
|
[--source-ref REF] Pin a configured-repository ref before admission
|
|
281
316
|
incident --file incident.json Submit a private, read-only incident draft
|
|
282
317
|
approve JOB_ID | cancel JOB_ID Review gate / stop this attempt
|
|
318
|
+
publish JOB_ID Publish/reconcile the accepted candidate as one draft PR
|
|
319
|
+
abandon-delivery JOB_ID --branch-sha SHA Resolve an inspected pre-write collision; keep the remote branch
|
|
283
320
|
retry JOB_ID Prove stop; retain old checkout and retry
|
|
284
321
|
revise JOB_ID --file feedback.md [--source-ref REF]
|
|
285
322
|
New build/check/review; source stays pinned unless a new ref is explicit
|
|
@@ -291,7 +328,7 @@ Runtime commands accept --state PATH. Default: ${DEFAULT_STATE}
|
|
|
291
328
|
Demo default: ${DEFAULT_DEMO_STATE}
|
|
292
329
|
The npm CLI keeps state outside the package; updates wait for stopped installations.
|
|
293
330
|
Dashboard binds only to loopback; use SSH for remote access.
|
|
294
|
-
Setup plan: ${join(ROOT, 'docs/setup.md')}
|
|
331
|
+
Setup plan: ${join(ROOT, 'docs/setup.md')}
|
|
295
332
|
See docs/quickstart.md for task execution, evidence and recovery.`);
|
|
296
333
|
else throw new Error(`Unknown command: ${command}`);
|
|
297
334
|
} catch(error) {console.error(`Factory: ${error.message}`);process.exitCode=1;}
|
package/docs/interfaces.md
CHANGED
|
@@ -33,7 +33,7 @@ shell endpoint or a second scheduler.
|
|
|
33
33
|
| Method export | `kit --output` | No export endpoint | None | Equivalent download/export preserving staging-only adoption |
|
|
34
34
|
| Synthetic qualification | `demo`, `qualify` | No qualification endpoint | Synthetic disclosure only | Explicit separate state; never target an application accidentally |
|
|
35
35
|
| Immutable source admission | `init --source-ref`, `run --source-ref`, `issue start --source-ref`; status and build evidence carry the resolved SHA | `POST /api/v1/jobs` resolves/retains before acknowledgement; shared source metadata in status | New issue and revision forms accept a ref; task detail shows requested ref, resolved SHA and prior source commits | Build/retry use retained objects; revisions keep the recorded source unless a new ref is explicit; legacy source remains unknown |
|
|
36
|
-
| Trusted PR handoff |
|
|
36
|
+
| Trusted PR handoff | `publish JOB_ID` publishes/reconciles; `abandon-delivery JOB_ID --branch-sha SHA` records a checked local resolution for a pre-write branch collision | Authenticated `POST /api/v1/jobs/:id/publish` and `/abandon-delivery`; shared receipt, conflict identity and removal policy | Publish/reconcile and explicit “Abandon local delivery; keep remote branch” actions share controller state; errors/results and inspected branch identity are visible | New writes require matching protected Codex/Pi build/review provenance, deterministic verify/handoff provenance, non-synthetic bound artifacts and a qualified GitHub Actions tree. Shared delivery status exposes `workflow_qualification` and the same reason blocks CLI/API/dashboard capability and publication/retry. Candidate workflow changes, unsupported triggers/syntax, or active generated-push, selected-ref-dispatch and PR jobs with write/secrets/environment/OIDC/deploy access, self-hosted runners or ambiguous privileged guards refuse trusted writes. Supported ASCII guard comparisons follow GitHub's case-insensitive string semantics; unknown PR refs, non-ASCII mismatches, and glob/escaped branch filters cannot prove a privileged job inactive. The shared summary's `action_mode` distinguishes new/resumable publication from read-only reconciliation and drives idle and pending task button wording. Branch-only collisions and unknown/abandoned states offer neither; known PR receipts and pending PR-creation checkpoints retain read-only reconciliation. Abandonment checks the current run, saved intent, exact branch head and absence of an associated PR; it writes no provider data, preserves the remote branch/evidence, disables republishing and permits local removal. Uncertain effects and incompatible evidence stay blocked. Destination remains private operator config; patch-only remains default |
|
|
37
37
|
|
|
38
38
|
The current generic task form can name the Defence workflow; that is not a
|
|
39
39
|
substitute for the CLI's validated incident admission. Treat the typed intake
|
package/docs/quickstart.md
CHANGED
|
@@ -28,15 +28,47 @@ software-defence-factory install --state /private/state/my-app
|
|
|
28
28
|
software-defence-factory doctor --state /private/state/my-app
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
`init --source-ref` selects the configured default ref (`HEAD` when omitted). Each job resolves that ref, or an explicit `--source-ref` on `run`/`issue start`, in the configured repository and durably retains its commit before acknowledging admission.
|
|
31
|
+
`init --source-ref` selects the configured default ref (`HEAD` when omitted). Each job resolves that ref, or an explicit `--source-ref` on `run`/`issue start`, in the configured repository and durably retains its commit before acknowledging admission. It records the canonical GitHub origin identity when available; the CLI and dashboard show the requested ref and resolved SHA. Task text and reference links do not select a repository, source ref or PR target.
|
|
32
32
|
|
|
33
33
|
Verification commands receive `FACTORY_BASE_REVISION`, the resolved admission commit recorded as the candidate base. Diff-based checks should compare against this revision; the isolated checkout has no origin remote. The value comes from protected controller metadata, not the task text.
|
|
34
34
|
|
|
35
35
|
Replace the check with the application's actual verification command. `init` does not edit the app, copy global skills or start work. It creates factory.json, worker.token and model.env with private permissions. Each installation has one repository and a distinct state path/port. `--harness pi` selects Pi; `--harness custom --command-json '["executable","argument"]'` selects an available command in the job image. The bundled image provides Node, Git, Codex and Pi. Other toolchains require an intentionally built compatible image; do not claim Rust/mobile/browser capabilities from this image alone.
|
|
36
36
|
|
|
37
|
-
Configure inference
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
Configure supported inference settings in the private `model.env` file. It
|
|
38
|
+
rejects unrelated names such as `DEPLOY_TOKEN`; do not copy the operator's
|
|
39
|
+
account environment or authentication folders. Codex receives only its OpenAI
|
|
40
|
+
settings. Pi receives only the selected provider's settings. Set
|
|
41
|
+
`--inference-provider PROVIDER` during `init`, or pin an operator-selected
|
|
42
|
+
`provider/model` with `--model`; if multiple provider credential groups are in
|
|
43
|
+
`model.env`, an explicit provider is required. Task-level model overrides never
|
|
44
|
+
select a credential group. See the `inferenceProvider` entry in private
|
|
45
|
+
`factory.json` when editing trusted installation configuration directly.
|
|
46
|
+
|
|
47
|
+
Installations using the existing Codex account-auth command may also put its
|
|
48
|
+
single-line JSON object in `FACTORY_CODEX_AUTH_JSON` in private `model.env`.
|
|
49
|
+
Factory validates that value as JSON and gives it only to the selected Codex
|
|
50
|
+
build/review/defence worker through a temporary private env file; the configured
|
|
51
|
+
operator command that consumes this setting must materialize its temporary
|
|
52
|
+
native `auth.json` before invoking Codex; the bundled stock `codex exec` does
|
|
53
|
+
not do this by itself. Pi never receives this setting, including with the OpenAI provider, and
|
|
54
|
+
deterministic checks and output artifacts do not receive it. Keep `model.env`
|
|
55
|
+
private with mode `0600`; do not copy account folders or place the value in a
|
|
56
|
+
task, source file or report. This setting does not replace provider API-key or
|
|
57
|
+
local OpenAI-compatible endpoint configuration.
|
|
58
|
+
|
|
59
|
+
Factory's Pi provider identifiers are `anthropic`, `azure-openai-responses`,
|
|
60
|
+
`cerebras`, `cloudflare-ai-gateway`, `cloudflare-workers-ai`, `deepseek`,
|
|
61
|
+
`google`, `groq`, `huggingface`, `kimi-coding`, `minimax`, `minimax-cn`,
|
|
62
|
+
`mistral`, `openai`, `opencode`, `opencode-go`, `openrouter`,
|
|
63
|
+
`vercel-ai-gateway`, `xiaomi`, `xiaomi-token-plan-ams`,
|
|
64
|
+
`xiaomi-token-plan-cn`, `xiaomi-token-plan-sgp`, `xai` and `zai`. Factory
|
|
65
|
+
forwards provider API-key and endpoint settings from this map; it does not
|
|
66
|
+
forward OAuth files or ambient cloud identity credentials.
|
|
67
|
+
|
|
68
|
+
`OPENAI_BASE_URL` remains available for an OpenAI-compatible local endpoint
|
|
69
|
+
selected for Codex; the endpoint must be reachable from inside the job
|
|
70
|
+
container. Host loopback addresses do not automatically refer to the host from
|
|
71
|
+
Docker. The package does not automatically expose Ollama or import models.
|
|
40
72
|
|
|
41
73
|
```sh
|
|
42
74
|
software-defence-factory up --state /private/state/my-app
|
|
@@ -51,6 +83,86 @@ Inspect the task's Result, Files and History tabs. Task details show the request
|
|
|
51
83
|
|
|
52
84
|
The source application is not changed and no branch, PR, merge or deployment is published automatically. A reviewed change.patch can be checked and applied with `git apply --check` and `git apply` on an appropriate branch at its recorded base revision; then follow the application's normal integrated checks and delivery policy.
|
|
53
85
|
|
|
86
|
+
### Optional trusted PR delivery
|
|
87
|
+
|
|
88
|
+
Patch-only remains the default. To enable the first delivery provider, select a
|
|
89
|
+
canonical GitHub origin and an explicit target while initializing the private
|
|
90
|
+
installation:
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
software-defence-factory init --repo /absolute/path/to/app --harness codex \
|
|
94
|
+
--check "npm ci && npm test" --source-ref main \
|
|
95
|
+
--delivery-provider github \
|
|
96
|
+
--delivery-repository https://github.com/OWNER/REPO \
|
|
97
|
+
--delivery-target main --state /private/state/my-app
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Use `dev` only when it is the intended target. The source ref (`main` above)
|
|
101
|
+
and PR target are separate settings. The configured GitHub repository must
|
|
102
|
+
match the canonical origin captured at admission; a later repository rename or
|
|
103
|
+
remote change blocks delivery. Configure this before admitting work because a
|
|
104
|
+
configuration change invalidates earlier check/review/approval policy evidence.
|
|
105
|
+
Unknown providers and installations without `delivery` configuration keep the
|
|
106
|
+
patch-only flow.
|
|
107
|
+
|
|
108
|
+
After the ordinary check, independent review and operator approval complete,
|
|
109
|
+
use **Publish accepted candidate as draft PR** in task details or run:
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
software-defence-factory publish JOB_ID --state /private/state/my-app
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Trusted publication requires protected per-run execution records for native
|
|
116
|
+
Codex/Pi build and review plus deterministic verification and handoff, bound to
|
|
117
|
+
non-synthetic candidate, check and review artifacts. Mock qualification remains
|
|
118
|
+
local exploration and cannot be published; missing or inconsistent provenance
|
|
119
|
+
keeps the action unavailable in status, CLI, API and dashboard. A saved intent
|
|
120
|
+
is checked again before new provider writes. Known PR receipts and PR-creation
|
|
121
|
+
checkpoints still allow read-only reconciliation.
|
|
122
|
+
|
|
123
|
+
New writes also require the shared GitHub Actions qualification described in
|
|
124
|
+
[setup](setup.md#optional-trusted-pr-delivery) and [recovery](recovery.md#trusted-pr-delivery).
|
|
125
|
+
Unsupported or candidate-changed workflows keep publication unavailable in
|
|
126
|
+
status, CLI, API and dashboard; the accepted patch remains available for normal
|
|
127
|
+
manual delivery.
|
|
128
|
+
|
|
129
|
+
For `push`, the qualifier evaluates branch filters when either `branches` or
|
|
130
|
+
`branches-ignore` is declared, even alongside tag filters; tag-only filters do
|
|
131
|
+
not activate a generated branch push. Docker phase logs redact literal selected
|
|
132
|
+
inference credential values before retention. Mounted worker reports are
|
|
133
|
+
redacted after the container stops and before promotion; if shutdown is
|
|
134
|
+
uncertain, recovery handles them after confirming the stop. This is a bounded
|
|
135
|
+
output filter, not general protection against encoded or transformed values.
|
|
136
|
+
|
|
137
|
+
The controller uses its existing `gh` identity. GitHub credentials are never
|
|
138
|
+
copied to `model.env` or mounted into jobs. The intent, generated branch, PR
|
|
139
|
+
identity, actual base/head/tree and triggered PR check results appear in the
|
|
140
|
+
same job. Unknown and pending checks remain visible and are not reported as
|
|
141
|
+
success. GitHub's raw `success`, `skipped` and `neutral` conclusions are
|
|
142
|
+
non-blocking in the Factory check summary; skipped and neutral remain visibly
|
|
143
|
+
distinct from an executed passing check. Unknown conclusions and incomplete
|
|
144
|
+
pagination keep the aggregate unknown. This summary does not determine branch
|
|
145
|
+
protection requirements or grant merge authorization. See [GitHub's required
|
|
146
|
+
status check guidance](https://docs.github.com/en/pull-requests/how-tos/merge-and-close-pull-requests/troubleshooting-required-status-checks).
|
|
147
|
+
Repeating `publish` refreshes/reconciles the same branch and PR; it does not
|
|
148
|
+
create a second PR or overwrite a changed branch. The CLI gives this bounded
|
|
149
|
+
multi-request action ten minutes; branch resolution uses the same bound and
|
|
150
|
+
other API calls keep their five-second deadline. If a client deadline expires,
|
|
151
|
+
inspect status and repeat `publish` to reconcile or recheck the reported branch
|
|
152
|
+
identity before `abandon-delivery`. Delete issue stays disabled while delivery
|
|
153
|
+
is unresolved, and the controller enforces the same guard on its API.
|
|
154
|
+
Publication does not merge, integrate, release or deploy. See [delivery
|
|
155
|
+
recovery](recovery.md#trusted-pr-delivery).
|
|
156
|
+
|
|
157
|
+
If status reports a reserved branch collision at the untouched `intent` stage,
|
|
158
|
+
inspect that branch in GitHub first. The task detail action **Abandon local
|
|
159
|
+
delivery; keep remote branch** or `abandon-delivery JOB_ID --branch-sha SHA`
|
|
160
|
+
records only a local resolution after the controller confirms the same branch
|
|
161
|
+
head and no associated PR. It preserves the remote branch and accepted evidence,
|
|
162
|
+
permits local issue removal, and permanently disables publication for that
|
|
163
|
+
delivery record. Changed identities, PRs and uncertain provider effects remain
|
|
164
|
+
blocked; see [recovery](recovery.md#trusted-pr-delivery).
|
|
165
|
+
|
|
54
166
|
## Remote access and operation
|
|
55
167
|
|
|
56
168
|
```sh
|
|
@@ -109,8 +221,11 @@ is tested at 100%; changing browser zoom is separate from a project theme.
|
|
|
109
221
|
## Environment
|
|
110
222
|
|
|
111
223
|
Factory does not load a repository `.env` file. Configure the private
|
|
112
|
-
`factory.json` through `init`; put inference
|
|
113
|
-
`model.env`. A
|
|
224
|
+
`factory.json` through `init`; put supported inference settings only in its
|
|
225
|
+
private `model.env`. A provider allowlist selects the configured Codex/Pi
|
|
226
|
+
settings before a worker starts; unrelated host, forge, deployment, cloud
|
|
227
|
+
identity and application variables are rejected. A repository `.env.example`
|
|
228
|
+
is unnecessary for this CLI. Optional
|
|
114
229
|
process settings are `SDF_AUTO_UPDATE=0` (skip automatic CLI update checks),
|
|
115
230
|
`XDG_STATE_HOME`, `XDG_DATA_HOME` and `XDG_CONFIG_HOME` (user-owned state, release
|
|
116
231
|
and service locations). They must be exported in the process environment.
|
package/docs/recovery.md
CHANGED
|
@@ -6,7 +6,8 @@ Use `status --state PATH` and the private supervisor.log to identify the active
|
|
|
6
6
|
- An unconfirmed running attempt becomes `interrupted` on controller restart. It is never silently considered successful.
|
|
7
7
|
- Admission resolves the configured source ref or an explicit `--source-ref` in the configured repository, records its identity/ref/SHA in private job metadata and retains its Git objects under that job. Build and retry restore from this retained revision; moving or deleting the source ref does not select a new commit.
|
|
8
8
|
- `retry JOB_ID` verifies retained objects and reconciles the previous process group and containers. A missing/corrupt retained source, live writer or unknown process blocks retry. For a new build/defence attempt, the prior checkout is retained as previous-checkout-*.
|
|
9
|
-
- A
|
|
9
|
+
- A forced host/controller stop can leave the selected inference env file in the private attempt directory. Normal executor cleanup removes it only after a Docker listing confirms the named container is absent. Retry/reconciliation removes a leftover copy only after the prior process group is confirmed stopped and a Docker listing confirms the labelled containers are absent; a `docker rm` or `docker stop` exit alone does not prove shutdown. If identity, a Docker probe or shutdown is uncertain, the recovery fence and file remain; do not clean it manually while a worker may still be active. After shutdown is confirmed, recovery also redacts the fixed phase reports and log using the selected environment values before removing the env file.
|
|
10
|
+
- Retry repeats the stopped phase. A failed verification may retry the same candidate when its policy is unchanged and the check can now pass. If the check or execution policy changed after build, retrying verify/review cannot reuse the earlier build for handoff; handoff remains blocked. Preserve that failed attempt, then use an eligible requested revision or submit a replacement task to build under the current policy. Do not edit SQLite or acceptance evidence to bypass the guard.
|
|
10
11
|
- A requested revision keeps the recorded source by default, retains previous evidence and starts a new build with accumulated feedback. Supplying a deliberate new `--source-ref` resolves and retains that base before the action; prior source metadata stays in history, and the new build gets fresh checks and review.
|
|
11
12
|
- Removing a stopped task from the dashboard hides its queue record. Private artifacts and its deleted_at record remain on disk; this is not secure erasure.
|
|
12
13
|
|
|
@@ -17,9 +18,14 @@ For backup, stop the installation and copy the complete private state directory,
|
|
|
17
18
|
Earlier experimental engines use a different journal. Start a new state directory for the native 0.3 runtime; preserve old journals separately. There is no automatic import of their jobs or approval state.
|
|
18
19
|
|
|
19
20
|
Verification cleanup makes owned scratch directories traversable before removing
|
|
20
|
-
them and never follows their symlinks.
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
them and never follows their symlinks. The executor and recovery paths remove
|
|
22
|
+
`check-workspace` only after Docker confirms the named container is absent; a
|
|
23
|
+
client exit, failed remove or failed/unknown probe does not establish shutdown.
|
|
24
|
+
While shutdown is uncertain, the scratch directory and active recovery fence
|
|
25
|
+
remain. Ordinary reconciliation removes scratch after the process and labelled
|
|
26
|
+
containers are confirmed stopped. If a filesystem error still prevents cleanup,
|
|
27
|
+
the attempt fails and retains the original check exit and private log path
|
|
28
|
+
alongside the cleanup error.
|
|
23
29
|
Inspect that retained attempt before manual removal; never substitute the source
|
|
24
30
|
candidate path for the scratch path.
|
|
25
31
|
|
|
@@ -67,6 +73,24 @@ agent reports, passing checks or review. Nonzero exits and missing/malformed
|
|
|
67
73
|
reports still fail closed. Process termination during a host crash can leave
|
|
68
74
|
incomplete logs and an interrupted attempt; apply process reconciliation above.
|
|
69
75
|
|
|
76
|
+
Before retention, Docker stdout/stderr logs have exact selected inference
|
|
77
|
+
credential values replaced. Mounted reports (`agent-report.md`, `review.json`,
|
|
78
|
+
`incident-report.json`) are sanitized after the phase container is confirmed
|
|
79
|
+
absent; if shutdown is uncertain, they remain in the private attempt directory
|
|
80
|
+
and ordinary recovery sanitizes them after confirming the stop. Filtering
|
|
81
|
+
includes selected API-key, token, secret, password and credential settings plus
|
|
82
|
+
credential fields inside selected Codex auth JSON. Reports are opened without
|
|
83
|
+
following symlinks and with nonblocking access before descriptor type checks, so
|
|
84
|
+
special files such as FIFOs are refused promptly. An unsafe or oversized report
|
|
85
|
+
cannot be promoted. If filtering cannot complete, recovery retains the selected
|
|
86
|
+
inference file and active fence; a repeated recovery remains blocked until the
|
|
87
|
+
fixed owned report can be safely filtered. Once process and container shutdown
|
|
88
|
+
are confirmed, repair or replace that report and retry ordinary recovery so it
|
|
89
|
+
can sanitize the output before removing the selected file and fence. This
|
|
90
|
+
bounded filter does not detect encoded or transformed values and does not
|
|
91
|
+
rewrite candidate files or patches; it is not a general data loss prevention
|
|
92
|
+
control.
|
|
93
|
+
|
|
70
94
|
## Recorded execution profiles
|
|
71
95
|
|
|
72
96
|
Before each attempt executes, the controller freezes its effective configuration
|
|
@@ -126,3 +150,159 @@ Use `issue submissions` and `issue recover --key REQUEST_ID` against the same
|
|
|
126
150
|
controller state. Recovery reads the original provider using the original
|
|
127
151
|
identity; it does not publish again. Do not use a new request key to retry an
|
|
128
152
|
uncertain write. See [provider ownership and recovery limits](integrations.md).
|
|
153
|
+
|
|
154
|
+
## Trusted PR delivery
|
|
155
|
+
|
|
156
|
+
Delivery is available only for an accepted software job with current source,
|
|
157
|
+
candidate, check, independent review and approval records. Trusted publication
|
|
158
|
+
also requires matching private per-run execution records: native Codex/Pi for
|
|
159
|
+
build and review, deterministic verification and handoff, and explicitly
|
|
160
|
+
non-synthetic bound candidate/check/review artifacts. Missing, unknown,
|
|
161
|
+
synthetic or inconsistent evidence is not publication proof. Status, CLI, API
|
|
162
|
+
and dashboard use the same guard. The private operator configuration pins the
|
|
163
|
+
GitHub repository and target; task text and worker reports cannot provide
|
|
164
|
+
either. The controller stores the intent and remote branch/PR identifiers in
|
|
165
|
+
the job row before it writes. Status and task details show the receipt
|
|
166
|
+
separately from integration or deployment.
|
|
167
|
+
|
|
168
|
+
Inspect the same installation before recovery:
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
software-defence-factory status --state /private/state/project
|
|
172
|
+
software-defence-factory publish JOB_ID --state /private/state/project
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
After a lost response or controller restart, repeat `publish`. The controller
|
|
176
|
+
reads the exact configured target, generated branch and PR before attempting a
|
|
177
|
+
missing stage. A matching branch/PR is reused; a different remote head, target,
|
|
178
|
+
repository or collision is preserved and blocks recovery. It never force-pushes,
|
|
179
|
+
rebases, creates a second PR to avoid an uncertain response, or merges. Restore
|
|
180
|
+
the original trusted repository/target configuration if it changed; do not
|
|
181
|
+
redirect a saved intent.
|
|
182
|
+
|
|
183
|
+
Before any new content, branch or PR write, a saved intent is rechecked against
|
|
184
|
+
the protected run profiles and linked artifacts. A mock/synthetic record cannot
|
|
185
|
+
authorize a new write on retry. A known receipt or PR-creation checkpoint can
|
|
186
|
+
still use read-only provider reconciliation; if that cannot establish the
|
|
187
|
+
existing effect, the uncertain record remains blocked from further writes.
|
|
188
|
+
|
|
189
|
+
Before advertising or attempting a new write, status and the shared publisher
|
|
190
|
+
qualify the exact accepted Git trees again. Factory reads the immutable admitted
|
|
191
|
+
base commit and reconstructs the accepted candidate tree from the protected,
|
|
192
|
+
digest-bound patch. Added, changed, deleted or symlinked
|
|
193
|
+
`.github/workflows/*.yml` and `*.yaml` definitions block trusted publication.
|
|
194
|
+
Every base workflow must parse with the bundled YAML parser and use the
|
|
195
|
+
supported literal `push`, `pull_request`, `pull_request_target` or
|
|
196
|
+
`workflow_dispatch` trigger subset; dispatch inputs and other events such as
|
|
197
|
+
`workflow_run` or `workflow_call` are unsupported. A manual dispatch is
|
|
198
|
+
evaluated with the generated branch as its selected ref, as GitHub permits
|
|
199
|
+
dispatching a workflow against a selected branch ([manual workflow runs](https://docs.github.com/en/actions/how-tos/manage-workflow-runs/manually-run-a-workflow)).
|
|
200
|
+
Any declared PR activity type is treated as potentially active for that
|
|
201
|
+
candidate PR. Branch filters are interpreted only when their patterns contain
|
|
202
|
+
ASCII letters, digits, `.`, `_`, `-` or `/`; exact literals are checked against
|
|
203
|
+
the generated branch and configured target. Other patterns are treated as
|
|
204
|
+
possible matches. This includes `*`, `**`, `?`, `+`, `[]`, `!` and escaped
|
|
205
|
+
characters from GitHub's [filter syntax](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#filter-pattern-cheat-sheet).
|
|
206
|
+
Path filters are also treated as possible matches.
|
|
207
|
+
|
|
208
|
+
Conditions support only `&&`-joined `==`/`!=` comparisons against quoted
|
|
209
|
+
strings using `github.ref`, `github.event_name`, `github.head_ref`,
|
|
210
|
+
`github.base_ref` or `vars.NAME`; `||`, functions and other contexts are
|
|
211
|
+
refused. Supported ASCII string comparisons follow GitHub's case-insensitive
|
|
212
|
+
[expression semantics](https://docs.github.com/en/actions/reference/workflows-and-actions/expressions).
|
|
213
|
+
Non-ASCII mismatches stay unknown rather than guessing Unicode case folding.
|
|
214
|
+
For `pull_request`, `github.ref` stays unknown because the future merge ref
|
|
215
|
+
cannot be established from a guessed PR number. Unknown comparisons cannot
|
|
216
|
+
prove a privileged job inactive. Dynamic permissions, malformed YAML and
|
|
217
|
+
ambiguous guards block publication with a reason while leaving patch delivery
|
|
218
|
+
available.
|
|
219
|
+
|
|
220
|
+
Each job that could run for the generated branch push, a branch-selected
|
|
221
|
+
manual dispatch or a PR event must have explicit effective permissions limited
|
|
222
|
+
to `contents: read` or `none`, a known GitHub-hosted runner, and no secret
|
|
223
|
+
context, protected environment, reusable workflow call, OIDC permission or
|
|
224
|
+
deployment permission. A privileged release job can qualify only when a simple
|
|
225
|
+
literal conjunction proves it cannot run for each matching event. The current
|
|
226
|
+
`ci.yml` main-only release guard is supported. Before any content, branch or PR
|
|
227
|
+
write, the existing provider readback still confirms that the configured
|
|
228
|
+
target commit and tree are the exact accepted base. Active jobs must use one of
|
|
229
|
+
the supported literal runner labels (`ubuntu-latest`,
|
|
230
|
+
`ubuntu-22.04`, `ubuntu-24.04`, `ubuntu-26.04`, `windows-latest`,
|
|
231
|
+
`windows-2022`, `windows-2025`, `macos-latest`, `macos-14`, `macos-15` or
|
|
232
|
+
`macos-26`). External action steps must be local or pinned to an immutable
|
|
233
|
+
commit SHA or container digest.
|
|
234
|
+
|
|
235
|
+
This bounded check does not establish that all CI is safe. It does not inspect
|
|
236
|
+
repository/organization rules, webhooks, external CI or other organization
|
|
237
|
+
automation, nor does it audit the behavior of referenced third-party actions.
|
|
238
|
+
Unsupported cases refuse trusted publication and leave normal local checks,
|
|
239
|
+
review, approval and manual patch delivery available.
|
|
240
|
+
|
|
241
|
+
After publication is confirmed, readback follows the saved PR identity and
|
|
242
|
+
records its current open draft/ready, closed or merged state and checks. This
|
|
243
|
+
read-only refresh does not require the source branch to remain after closure or
|
|
244
|
+
the PR base commit to equal today's target commit. It still checks the saved
|
|
245
|
+
repository, target and branch names, PR head, and accepted delivery commit's
|
|
246
|
+
tree and parent. A changed identity or candidate stays a visible conflict; a
|
|
247
|
+
provider outage retains the last known publication receipt. First publication
|
|
248
|
+
continues to require the accepted current base and an open draft PR.
|
|
249
|
+
|
|
250
|
+
The dashboard offers reconciliation for saved `intent`, `publishing`,
|
|
251
|
+
`uncertain` and safely retryable `blocked` checkpoints. Legacy accepted jobs
|
|
252
|
+
without candidate-bound check, review and approval evidence are labelled
|
|
253
|
+
unverified and cannot be published. Deleting an issue with any unconfirmed
|
|
254
|
+
delivery record is blocked in the dashboard and controller API; inspect or
|
|
255
|
+
reconcile the saved remote effect before removing the job. A confirmed
|
|
256
|
+
`published` receipt is no longer unresolved.
|
|
257
|
+
|
|
258
|
+
The shared capability advertises new publication only with ready evidence and
|
|
259
|
+
no saved delivery or a known resumable write state. A branch-only `conflict`
|
|
260
|
+
has no publish action; inspect it and use explicit local abandonment only
|
|
261
|
+
after the exact branch head and absence of an associated PR are confirmed.
|
|
262
|
+
Conflicts with a saved PR receipt and pending PR-creation checkpoints retain a
|
|
263
|
+
read-only reconciliation action. Unknown and abandoned delivery states cannot
|
|
264
|
+
advertise or start publication.
|
|
265
|
+
|
|
266
|
+
The shared delivery status marks each available PR action as `publish` or
|
|
267
|
+
`reconcile`; task details use that mode for idle and pending button wording.
|
|
268
|
+
While a request is pending, publication shows “Publishing…” and read-only
|
|
269
|
+
recovery shows “Reconciling…”. A conflicted record with a saved PR receipt
|
|
270
|
+
therefore stays visibly a readback action.
|
|
271
|
+
|
|
272
|
+
A collision found while the record is still at `intent` is known to precede
|
|
273
|
+
provider writes. Inspect the exact branch in GitHub, then use the task action
|
|
274
|
+
**Abandon local delivery; keep remote branch**, or confirm its current head with
|
|
275
|
+
the CLI:
|
|
276
|
+
|
|
277
|
+
```sh
|
|
278
|
+
software-defence-factory status --state /private/state/project
|
|
279
|
+
software-defence-factory abandon-delivery JOB_ID --branch-sha INSPECTED_SHA --state /private/state/project
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The controller checks the latest run, saved delivery identity, current branch
|
|
283
|
+
head and exact repository/target, then confirms that no PR is attached. It
|
|
284
|
+
records the inspected identity and local resolution in the job row. It does not
|
|
285
|
+
write, update or delete the remote branch or create/accept a PR. The `abandoned`
|
|
286
|
+
state disables later publication and permits local issue removal while retaining
|
|
287
|
+
the delivery record, run history and evidence. A stale or changed branch, any
|
|
288
|
+
associated/unknown PR, changed destination, later delivery stage or uncertain
|
|
289
|
+
provider effect remains blocked; reconcile or inspect it before taking another
|
|
290
|
+
action.
|
|
291
|
+
|
|
292
|
+
CLI publication and branch-resolution requests have a ten-minute deadline
|
|
293
|
+
because they can require multiple provider requests. Other CLI API requests
|
|
294
|
+
keep their five-second deadline. If the client reaches its deadline, inspect
|
|
295
|
+
`status` after the controller action finishes. Repeat `publish` to reconcile a
|
|
296
|
+
publication; repeat `abandon-delivery` only after inspecting the currently
|
|
297
|
+
reported branch identity.
|
|
298
|
+
|
|
299
|
+
The receipt reads the exact PR head's check runs and commit statuses. Raw
|
|
300
|
+
conclusions are retained. `success`, `skipped` and `neutral` do not make the
|
|
301
|
+
aggregate fail, while skipped/neutral are distinct from an executed passing
|
|
302
|
+
check. `pending`, `unknown`, failed and unavailable remain distinct; unknown
|
|
303
|
+
conclusions and incomplete pagination stay unknown. This aggregate does not
|
|
304
|
+
establish required-check completeness or merge authorization. A target/base,
|
|
305
|
+
candidate or policy change requires fresh applicable checks, review and
|
|
306
|
+
approval; do not edit SQLite or acceptance evidence to bypass the guard. A stale
|
|
307
|
+
target after branch creation leaves that unique branch for inspection and does
|
|
308
|
+
not open a PR. A PR does not imply integration or deployment.
|
package/docs/setup.md
CHANGED
|
@@ -155,11 +155,79 @@ model servers, public listeners, Docker socket mounts or whole account folders.
|
|
|
155
155
|
Cloud inference likewise needs a real provider/model connectivity check without
|
|
156
156
|
printing credentials. A model-list/health response is connectivity evidence;
|
|
157
157
|
qualifying model output requires a separately accepted bounded task.
|
|
158
|
+
Store only supported inference settings in `model.env`; jobs reject unrelated
|
|
159
|
+
names such as deployment credentials. Codex receives its OpenAI setting group,
|
|
160
|
+
and Pi receives only its operator-selected provider group. Use
|
|
161
|
+
`--inference-provider` with `init` when Pi's provider is not encoded in the
|
|
162
|
+
operator-configured model name or when multiple provider groups are stored.
|
|
163
|
+
An installation already using Codex account auth may set the validated
|
|
164
|
+
`FACTORY_CODEX_AUTH_JSON` single-line JSON value in private `model.env`; only
|
|
165
|
+
Codex agent phases receive it. Pi and checks do not. See the [quickstart
|
|
166
|
+
inference guidance](quickstart.md#connect-an-application) for constraints.
|
|
158
167
|
|
|
159
168
|
Checkpoint: record the exact source revision, image ID, check command, resource
|
|
160
169
|
limits, inference connectivity and CI result. Keep product controllers stopped
|
|
161
170
|
until their tasks are explicitly ready to run.
|
|
162
171
|
|
|
172
|
+
### Optional trusted PR delivery
|
|
173
|
+
|
|
174
|
+
Patch-only handoff is the default. When the operator intends to enable GitHub
|
|
175
|
+
delivery for this installation, configure the exact canonical repository and
|
|
176
|
+
target while initializing it, for example:
|
|
177
|
+
|
|
178
|
+
```sh
|
|
179
|
+
software-defence-factory init --repo /absolute/path/to/app --harness pi \
|
|
180
|
+
--check "npm ci && npm test" --source-ref main \
|
|
181
|
+
--delivery-provider github \
|
|
182
|
+
--delivery-repository https://github.com/OWNER/REPO \
|
|
183
|
+
--delivery-target main --state /private/state/my-app
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The admitted source ref and PR target are independent. Only `main` and `dev`
|
|
187
|
+
are supported target choices in this release. The destination must match the
|
|
188
|
+
canonical origin retained at admission. A changed/renamed origin, moved target
|
|
189
|
+
base or changed Factory policy blocks delivery until fresh applicable evidence
|
|
190
|
+
exists. Configure this before admitting work. Unknown providers remain
|
|
191
|
+
patch-only.
|
|
192
|
+
|
|
193
|
+
Before enabling trusted PR delivery, confirm the repository workflows fit the
|
|
194
|
+
bounded qualification in [recovery](recovery.md#trusted-pr-delivery). Factory
|
|
195
|
+
compares the immutable admitted base and accepted candidate trees, refuses any
|
|
196
|
+
candidate change to a GitHub Actions workflow, and requires jobs that can run
|
|
197
|
+
for generated-branch pushes, PR events or selected-ref manual dispatches to use
|
|
198
|
+
explicit `contents: read` or `none`, a known GitHub-hosted runner, and no
|
|
199
|
+
secrets, protected environments, OIDC or deploy permissions. Unsupported
|
|
200
|
+
workflow syntax keeps the patch-only path available. Organization hooks and
|
|
201
|
+
other external CI automation remain operator-owned and are not audited by this
|
|
202
|
+
check.
|
|
203
|
+
|
|
204
|
+
The operator's authenticated `gh` identity stays on the controller. Keep GitHub
|
|
205
|
+
credentials out of `model.env`, project files and worker containers. Do not add
|
|
206
|
+
deploy credentials or a Docker socket for PR delivery. Use only the existing
|
|
207
|
+
authorized repository and the repository access already approved for the
|
|
208
|
+
operator; do not create a new fixture repository or request broader access.
|
|
209
|
+
|
|
210
|
+
The release lead owns the live provider/browser qualification after installing
|
|
211
|
+
the published candidate separately. Use a separate private state against the
|
|
212
|
+
already authorized Factory repository and `main`; ordinary issue admission,
|
|
213
|
+
checks, independent review and approval must produce the disposable candidate.
|
|
214
|
+
Give its issue a title beginning **[Factory PR handoff proof]**, then use the
|
|
215
|
+
normal **Publish accepted candidate as draft PR** action. Record the job,
|
|
216
|
+
candidate SHA/tree, generated unique branch, exact draft PR, base/head/tree and
|
|
217
|
+
triggered check states. The CLI allows up to ten minutes for this multi-request
|
|
218
|
+
controller action; other CLI API requests retain their five-second deadline.
|
|
219
|
+
If the client deadline expires, inspect status and repeat `publish JOB_ID` so
|
|
220
|
+
the controller can reconcile its saved intent. Restart the installed controller
|
|
221
|
+
and repeat `publish JOB_ID`; verify the same branch and PR head are read back
|
|
222
|
+
and no second PR appears. Delete issue is disabled while the delivery is
|
|
223
|
+
unresolved, and the controller rejects the same removal through its API. Leave
|
|
224
|
+
pending/unknown checks labelled as such. After inspection, close the proof PR
|
|
225
|
+
without merging its fixture change. Do not publish a worker candidate from
|
|
226
|
+
inside its sandbox.
|
|
227
|
+
|
|
228
|
+
Mocks exercise controller and receipt behavior only. They are not live GitHub
|
|
229
|
+
publication or browser proof; record each separately in [qualification](proof.md).
|
|
230
|
+
|
|
163
231
|
## 5. Enable only the intended background services
|
|
164
232
|
|
|
165
233
|
A newly initialized state has no jobs. Before adopting older state, establish
|
|
@@ -242,6 +310,8 @@ Copy this private completion record into the installation's handoff:
|
|
|
242
310
|
| History and intentionally stopped products preserved | Pending | |
|
|
243
311
|
| Backups, logs, stop/update/rollback owner and guide | Pending | |
|
|
244
312
|
| First bounded application task | Not started | Separate task authority and proof |
|
|
313
|
+
| Optional trusted PR provider and target explicitly configured, or patch-only retained | Pending | |
|
|
314
|
+
| Release lead live disposable Factory draft PR/readback/restart/close proof | Not started | Separate from mocked provider tests |
|
|
245
315
|
|
|
246
316
|
Use Pass, Fail or Not applicable with a reason; never infer success from an
|
|
247
317
|
installed file. Keep host identities, credentials, raw logs and customer details
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { githubDeliveryProvider } from './providers/github-delivery.mjs';
|
|
2
|
+
|
|
3
|
+
// Provider selection is a trusted installation setting. Unknown providers do
|
|
4
|
+
// not get guessed URLs, credentials or a write path; they remain patch-only.
|
|
5
|
+
export function deliveryProvider(config, integrations = {}) {
|
|
6
|
+
const provider = config.delivery?.provider;
|
|
7
|
+
if (provider !== 'github') return { id: provider || null, supported: false };
|
|
8
|
+
return integrations.deliveryProvider || githubDeliveryProvider();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function deliveryProviderInfo(config, provider) {
|
|
12
|
+
const enabled = provider?.supported === true && config.delivery?.provider === provider.id;
|
|
13
|
+
return {
|
|
14
|
+
enabled,
|
|
15
|
+
mode: enabled ? 'trusted_pr' : 'patch_only',
|
|
16
|
+
provider: config.delivery?.provider || null,
|
|
17
|
+
repository: enabled ? config.delivery.repository : null,
|
|
18
|
+
target: enabled ? config.delivery.target : null,
|
|
19
|
+
};
|
|
20
|
+
}
|