linkedin-apply-assistant 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +72 -0
  2. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  3. package/.github/ISSUE_TEMPLATE/config_help.yml +49 -0
  4. package/.github/ISSUE_TEMPLATE/docs.yml +40 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.yml +45 -0
  6. package/.github/ISSUE_TEMPLATE/safety_compliance.yml +48 -0
  7. package/.github/PULL_REQUEST_TEMPLATE.md +43 -0
  8. package/CHANGELOG.md +47 -0
  9. package/CODE_OF_CONDUCT.md +47 -0
  10. package/CONTRIBUTING.md +64 -0
  11. package/GOVERNANCE.md +41 -0
  12. package/LEGAL.md +38 -0
  13. package/LICENSE +22 -0
  14. package/MIGRATION.md +50 -0
  15. package/README.md +167 -0
  16. package/RELEASE_CHECKLIST.md +454 -0
  17. package/SAFETY.md +33 -0
  18. package/SECURITY.md +37 -0
  19. package/SUPPORT.md +44 -0
  20. package/THIRD_PARTY_NOTICES.md +67 -0
  21. package/bin/linkedin-apply-assistant.mjs +95 -0
  22. package/configs/config.example.yml +24 -0
  23. package/configs/qa_bank.example.yml +35 -0
  24. package/docs/apply.md +40 -0
  25. package/docs/assist.md +35 -0
  26. package/docs/browser-session.md +45 -0
  27. package/docs/ci-and-release-policy.md +105 -0
  28. package/docs/commands.md +176 -0
  29. package/docs/install-and-configuration.md +265 -0
  30. package/docs/registry-publication-strategy.md +169 -0
  31. package/docs/reports.md +35 -0
  32. package/docs/search.md +39 -0
  33. package/docs/troubleshooting.md +57 -0
  34. package/examples/dry_run_input.example.json +25 -0
  35. package/examples/reports/apply-audit.example.json +31 -0
  36. package/examples/reports/search-report.example.json +40 -0
  37. package/install.ps1 +178 -0
  38. package/package.json +59 -0
  39. package/pyproject.toml +51 -0
  40. package/src/linkedin_apply_assistant/__init__.py +8 -0
  41. package/src/linkedin_apply_assistant/apply_reports.py +229 -0
  42. package/src/linkedin_apply_assistant/ats_handlers.py +217 -0
  43. package/src/linkedin_apply_assistant/browser_sessions.py +155 -0
  44. package/src/linkedin_apply_assistant/cli.py +570 -0
  45. package/src/linkedin_apply_assistant/config.py +109 -0
  46. package/src/linkedin_apply_assistant/contracts.py +255 -0
  47. package/src/linkedin_apply_assistant/form_engine.py +180 -0
  48. package/src/linkedin_apply_assistant/linkedin_layer.py +436 -0
  49. package/src/linkedin_apply_assistant/page_actions.py +110 -0
  50. package/src/linkedin_apply_assistant/page_selectors.py +88 -0
  51. package/src/linkedin_apply_assistant/paths.py +135 -0
  52. package/src/linkedin_apply_assistant/qa_bank.py +352 -0
  53. package/src/linkedin_apply_assistant/redaction.py +119 -0
  54. package/src/linkedin_apply_assistant/safety.py +230 -0
  55. package/src/linkedin_apply_assistant/workflows.py +435 -0
package/SECURITY.md ADDED
@@ -0,0 +1,37 @@
1
+ # Security Policy
2
+
3
+ ## Reporting Vulnerabilities
4
+
5
+ Use GitHub private vulnerability reporting for the standalone project when available.
6
+
7
+ If private reporting is not configured yet, open a maintainer-private channel before sharing exploit details publicly. A standalone security contact can be added here when the public repository is configured.
8
+
9
+ Do not use upstream personal contacts as the default standalone package security contact.
10
+
11
+ ## Reporting Routes
12
+
13
+ - Usage and setup support belongs in [SUPPORT.md](SUPPORT.md).
14
+ - Public safety/compliance concerns can use [.github/ISSUE_TEMPLATE/safety_compliance.yml](.github/ISSUE_TEMPLATE/safety_compliance.yml) only without exploit details.
15
+ - Conduct reports belong in [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) and should use private reporting.
16
+ - Vulnerability details stay in this security policy and should not be posted publicly.
17
+
18
+ ## What to Report
19
+
20
+ Report issues such as:
21
+
22
+ - credential or token exposure
23
+ - browser profile leakage
24
+ - report redaction failures
25
+ - unsafe submit behavior
26
+ - dependency vulnerabilities
27
+ - documentation that encourages unsafe platform behavior
28
+
29
+ ## Local Secret and Browser Profile Safety
30
+
31
+ Keep local config, Q&A banks, visible-browser profiles, outputs, reports, and private documents out of version control. The package `.gitignore` contains the expected local runtime patterns.
32
+
33
+ Never attach browser profiles, cookies, credentials, screenshots, private documents, generated local reports, or full private URLs to public issues.
34
+
35
+ ## Supported Versions
36
+
37
+ The initial standalone GitHub source release is `0.1.0`. Current package metadata is `0.1.1`. Security guidance applies to the current unreleased, `0.1.1`, and `0.1.0` package-local surfaces.
package/SUPPORT.md ADDED
@@ -0,0 +1,44 @@
1
+ # Support
2
+
3
+ LinkedIn-apply-assistant support is for the standalone local package and its
4
+ documented source, Python, and npm launcher workflows.
5
+
6
+ ## Before Opening an Issue
7
+
8
+ Start with [README.md](README.md) and
9
+ [docs/troubleshooting.md](docs/troubleshooting.md). For setup questions, run:
10
+
11
+ ```powershell
12
+ linkedin-apply-assistant config check
13
+ ```
14
+
15
+ Use the config/help issue form only after removing private local details.
16
+
17
+ ## Public Support Routes
18
+
19
+ - Reproducible bugs: open a GitHub issue with the sanitized command, package
20
+ version or source commit, operating system, Python version, expected result,
21
+ actual result, and minimal reproduction steps.
22
+ - Feature requests: describe the problem, proposed behavior, workflow impact,
23
+ safety/privacy impact, and alternatives considered.
24
+ - Documentation issues: name the affected page or link, explain what is stale
25
+ or confusing, and suggest the correction.
26
+
27
+ GitHub Discussions are not enabled for this repository in Phase 26.
28
+
29
+ ## Sensitive Routes
30
+
31
+ - Vulnerabilities belong in [SECURITY.md](SECURITY.md). Do not post exploit
32
+ details publicly.
33
+ - Safety or platform-compliance concerns can use the safety/compliance issue
34
+ form only when the concern can be described without exploit details or private
35
+ data.
36
+ - Conduct reports belong in [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) and should
37
+ use a maintainer-private channel placeholder until a dedicated private contact
38
+ is configured.
39
+
40
+ ## Do Not Post Private Data
41
+
42
+ Do not post credentials, cookies, browser profiles, screenshots, CVs, private
43
+ documents, generated local reports, full private URLs, or live job history in
44
+ public issues, pull requests, or discussions.
@@ -0,0 +1,67 @@
1
+ # Third-Party Notices
2
+
3
+ This file records attribution and notice information for the standalone package. It is an engineering notice artifact, not legal advice.
4
+
5
+ ## Career-Ops
6
+
7
+ Some implementation and documentation history was extracted from or derived from Career-Ops, an MIT-licensed project by Santiago Fernandez de Valderrama.
8
+
9
+ Career-Ops is mentioned here only for neutral attribution and provenance. LinkedIn-apply-assistant is the standalone package identity.
10
+
11
+ MIT notice:
12
+
13
+ ```text
14
+ MIT License
15
+
16
+ Copyright (c) 2026 Santiago Fernandez de Valderrama
17
+
18
+ Permission is hereby granted, free of charge, to any person obtaining a copy
19
+ of this software and associated documentation files (the "Software"), to deal
20
+ in the Software without restriction, including without limitation the rights
21
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
22
+ copies of the Software, and to permit persons to whom the Software is
23
+ furnished to do so, subject to the following conditions:
24
+
25
+ The above copyright notice and this permission notice shall be included in all
26
+ copies or substantial portions of the Software.
27
+ ```
28
+
29
+ ## Scrapling
30
+
31
+ Scrapling is a normal Python dependency of this package. It is not the product identity and is not an endorsement claim.
32
+
33
+ License verification source checked during Phase 18 execution: official upstream repository license at `https://raw.githubusercontent.com/D4Vinci/Scrapling/main/LICENSE`. The upstream license identifies Scrapling as BSD 3-Clause, copyright 2024 Karim shoair.
34
+
35
+ BSD 3-Clause notice:
36
+
37
+ ```text
38
+ BSD 3-Clause License
39
+
40
+ Copyright (c) 2024, Karim shoair
41
+
42
+ Redistribution and use in source and binary forms, with or without
43
+ modification, are permitted provided that the following conditions are met:
44
+
45
+ 1. Redistributions of source code must retain the above copyright notice, this
46
+ list of conditions and the following disclaimer.
47
+
48
+ 2. Redistributions in binary form must reproduce the above copyright notice,
49
+ this list of conditions and the following disclaimer in the documentation
50
+ and/or other materials provided with the distribution.
51
+
52
+ 3. Neither the name of the copyright holder nor the names of its contributors
53
+ may be used to endorse or promote products derived from this software without
54
+ specific prior written permission.
55
+
56
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
57
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
58
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
59
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
60
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
61
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
62
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
63
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
64
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
65
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
66
+ ```
67
+
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from "node:child_process";
4
+ import { existsSync } from "node:fs";
5
+ import { dirname, delimiter, resolve } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const cliModule = "linkedin_apply_assistant.cli";
9
+ const userArgs = process.argv.slice(2);
10
+ const scriptDir = dirname(fileURLToPath(import.meta.url));
11
+ const packageRoot = resolve(scriptDir, "..");
12
+ const localSrc = resolve(scriptDir, "..", "src");
13
+ const launcherEnv = { ...process.env };
14
+ const candidates =
15
+ process.platform === "win32"
16
+ ? [
17
+ ["py", ["-3"]],
18
+ ["python", []],
19
+ ["python3", []],
20
+ ]
21
+ : [
22
+ ["python3", []],
23
+ ["python", []],
24
+ ];
25
+
26
+ if (existsSync(localSrc)) {
27
+ launcherEnv.PYTHONPATH = launcherEnv.PYTHONPATH
28
+ ? `${localSrc}${delimiter}${launcherEnv.PYTHONPATH}`
29
+ : localSrc;
30
+ }
31
+
32
+ function printSetupGuidance(reason) {
33
+ console.error(`linkedin-apply-assistant npm launcher could not start: ${reason}`);
34
+ console.error("");
35
+ console.error("This npm package is only a thin launcher for the Python package.");
36
+ console.error("Install the bundled Python package from this npm package root, then retry:");
37
+ console.error(` python -m pip install "${packageRoot}"`);
38
+ console.error(` py -3 -m pip install "${packageRoot}"`);
39
+ console.error("");
40
+ console.error("Module fallback after source checkout:");
41
+ console.error(" python -m linkedin_apply_assistant.cli --help");
42
+ }
43
+
44
+ function runPython(command, args, options = {}) {
45
+ return spawnSync(command, args, {
46
+ env: launcherEnv,
47
+ windowsHide: true,
48
+ ...options,
49
+ });
50
+ }
51
+
52
+ let sawPython = false;
53
+
54
+ for (const [command, prefixArgs] of candidates) {
55
+ const probe = runPython(command, [...prefixArgs, "-c", `import ${cliModule}`], {
56
+ stdio: "ignore",
57
+ });
58
+
59
+ if (probe.error?.code === "ENOENT") {
60
+ continue;
61
+ }
62
+
63
+ if (probe.error) {
64
+ continue;
65
+ }
66
+
67
+ sawPython = true;
68
+
69
+ if (probe.status !== 0) {
70
+ continue;
71
+ }
72
+
73
+ const result = runPython(command, [...prefixArgs, "-m", cliModule, ...userArgs], {
74
+ stdio: "inherit",
75
+ });
76
+
77
+ if (result.error) {
78
+ printSetupGuidance(result.error.message);
79
+ process.exit(1);
80
+ }
81
+
82
+ if (result.signal) {
83
+ console.error(`Python CLI exited after signal ${result.signal}`);
84
+ process.exit(1);
85
+ }
86
+
87
+ process.exit(result.status ?? 1);
88
+ }
89
+
90
+ printSetupGuidance(
91
+ sawPython
92
+ ? "Python was found, but the linkedin_apply_assistant package is not importable."
93
+ : "No usable Python executable was found on PATH.",
94
+ );
95
+ process.exit(1);
@@ -0,0 +1,24 @@
1
+ profile:
2
+ name: "Example Candidate"
3
+ headline: "Software engineer"
4
+ location: "Example City"
5
+ contact:
6
+ email: "candidate@example.com"
7
+ phone: null
8
+ website: "https://example.com/portfolio"
9
+
10
+ defaults:
11
+ limit: 10
12
+ visible_browser: true
13
+ require_confirmation: true
14
+ dry_run: true
15
+
16
+ documents:
17
+ resume: "documents/resume.example.pdf"
18
+ cover_letter: "documents/cover-letter.example.pdf"
19
+ portfolio: "https://example.com/portfolio"
20
+
21
+ paths:
22
+ qa_bank: "configs/qa_bank.example.yml"
23
+ browser_profile: null
24
+ output_dir: "local-output"
@@ -0,0 +1,35 @@
1
+ qa_pairs:
2
+ - id: work_authorization
3
+ question_patterns:
4
+ - "Are you authorized to work in the target country?"
5
+ - "Do you require sponsorship?"
6
+ answer: "Use a truthful work-authorization answer for your situation."
7
+ response_type: text
8
+
9
+ - id: notice_period
10
+ question_patterns:
11
+ - "When can you start?"
12
+ - "What is your notice period?"
13
+ answer: "Use your current availability or notice period."
14
+ response_type: text
15
+
16
+ - id: work_arrangement
17
+ question_patterns:
18
+ - "Do you prefer remote, hybrid, or onsite work?"
19
+ - "What work arrangement are you looking for?"
20
+ answer: "Remote or hybrid roles are preferred, depending on team needs."
21
+ response_type: text
22
+
23
+ - id: compensation_expectation
24
+ question_patterns:
25
+ - "What are your salary expectations?"
26
+ - "What compensation range are you targeting?"
27
+ answer: "Use a realistic range for the role, location, and market."
28
+ response_type: text
29
+
30
+ - id: portfolio
31
+ question_patterns:
32
+ - "Please share a portfolio or project link."
33
+ - "Do you have examples of your work?"
34
+ answer: "https://example.com/portfolio"
35
+ response_type: url
package/docs/apply.md ADDED
@@ -0,0 +1,40 @@
1
+ # Prepare-Only Apply Workflow
2
+
3
+ `apply` prepares local application audit output from candidate job input. Browser submission remains disabled in the current public package.
4
+
5
+ ## Prepare From Input
6
+
7
+ Use synthetic or local candidate job input:
8
+
9
+ ```powershell
10
+ linkedin-apply-assistant apply --input examples\dry_run_input.example.json --limit 1
11
+ ```
12
+
13
+ The input file should not contain credentials, private documents, browser state, screenshots, full private URLs, or live job history.
14
+
15
+ ## Guarded Future Option
16
+
17
+ The CLI exposes `--confirm-submit` as a guarded future option:
18
+
19
+ ```powershell
20
+ linkedin-apply-assistant apply --input examples\dry_run_input.example.json --limit 1 --confirm-submit
21
+ ```
22
+
23
+ Current browser submission remains disabled. Any future submit-capable release must still require explicit per-submission confirmation immediately before a specific application is sent, and must preserve the safety guardrails described in [../SAFETY.md](../SAFETY.md).
24
+
25
+ ## Shared Options
26
+
27
+ `apply` accepts:
28
+
29
+ - `--workspace`
30
+ - `--config`
31
+ - `--qa-bank`
32
+ - `--browser-profile`
33
+ - `--output-dir`
34
+ - `--verbose`
35
+ - `--input`
36
+ - `--limit`
37
+ - `--confirm-submit`
38
+
39
+ Do not use `apply` for mass applications, unattended apply sessions, fake answers, CAPTCHA or MFA bypass, or continued automation after platform risk signals.
40
+
package/docs/assist.md ADDED
@@ -0,0 +1,35 @@
1
+ # Assistive Fill-Only Workflow
2
+
3
+ `assist` opens a visible-browser workflow where the user drives the session and the assistant fills supported fields. It is a fill-only boundary.
4
+
5
+ ## On-Demand Mode
6
+
7
+ Use on-demand mode when you want to control each fill attempt:
8
+
9
+ ```powershell
10
+ linkedin-apply-assistant assist --mode on-demand --max-cycles 3
11
+ ```
12
+
13
+ ## Auto-Watch Mode
14
+
15
+ Use auto-watch when you want the assistant to inspect detected fillable surfaces:
16
+
17
+ ```powershell
18
+ linkedin-apply-assistant assist --mode auto-watch --max-cycles 3
19
+ ```
20
+
21
+ ## Start Page
22
+
23
+ ```powershell
24
+ linkedin-apply-assistant assist --start-url "https://www.linkedin.com/jobs/" --mode on-demand
25
+ ```
26
+
27
+ ## Boundaries
28
+
29
+ - You remain responsible for the browser session.
30
+ - Unknown required questions should stop until you provide a truthful answer.
31
+ - The assistant must not submit applications in this workflow.
32
+ - The assistant must not continue through CAPTCHA, MFA, checkpoints, platform throttling, or similar risk signals.
33
+
34
+ For browser profile safety, see [browser-session.md](browser-session.md).
35
+
@@ -0,0 +1,45 @@
1
+ # Visible Browser Session Setup
2
+
3
+ Browser workflows are local and user-visible. You drive or review the browser session; the package does not run hidden unattended apply workflows.
4
+
5
+ ## Browser Profile
6
+
7
+ Use `--browser-profile` to choose a local profile directory:
8
+
9
+ ```powershell
10
+ linkedin-apply-assistant --browser-profile .\local-workspace\browser-profile assist --mode on-demand
11
+ ```
12
+
13
+ The profile can contain cookies, sessions, and local form state. Keep it ignored, local, and under your control. Do not copy it into examples, issues, fixtures, or reports.
14
+
15
+ ## Login Flow
16
+
17
+ Open a visible browser session and log in yourself when the platform asks for it. If a checkpoint, rate limit, MFA prompt, or other platform risk signal appears, stop and resolve it manually.
18
+
19
+ The assistant must not bypass CAPTCHA, MFA, checkpoints, platform throttling, or employer application rules.
20
+
21
+ ## Start URL
22
+
23
+ Use `assist --start-url` when you want the visible browser to open a specific starting page:
24
+
25
+ ```powershell
26
+ linkedin-apply-assistant assist --start-url "https://www.linkedin.com/jobs/" --mode on-demand
27
+ ```
28
+
29
+ Only use URLs you are comfortable opening in your own browser session. Do not publish full private application URLs in docs or examples.
30
+
31
+ ## Session Modes
32
+
33
+ `assist` supports:
34
+
35
+ - `--mode on-demand`: inspect/fill only when requested by the user workflow.
36
+ - `--mode auto-watch`: watch for fillable surfaces and fill once per detected surface.
37
+
38
+ Example:
39
+
40
+ ```powershell
41
+ linkedin-apply-assistant assist --mode auto-watch --max-cycles 3
42
+ ```
43
+
44
+ Both modes remain fill-only boundaries. They do not submit applications.
45
+
@@ -0,0 +1,105 @@
1
+ # CI and Release Policy
2
+
3
+ This repository uses CI to make public project health visible without creating
4
+ tags, GitHub Releases, package uploads, registry tokens, attestations, or
5
+ repository-setting changes.
6
+
7
+ ## Workflows
8
+
9
+ Two user-authored GitHub Actions workflows are expected on `main`:
10
+
11
+ - `Quality` at `.github/workflows/quality.yml`
12
+ - `Security` at `.github/workflows/security.yml`
13
+
14
+ Both workflows run on pull requests, pushes to `main`, and manual dispatch. Only
15
+ `Security` has a weekly scheduled scan. Both workflows use concurrency with
16
+ `cancel-in-progress` so stale branch runs do not consume runner capacity.
17
+
18
+ ## Quality
19
+
20
+ The `Quality` workflow has two jobs:
21
+
22
+ - `quality` runs on Ubuntu with Python `3.11` and `3.12`.
23
+ - `release-smoke` runs once with Python `3.12` and Node.js `24`.
24
+
25
+ The workflow installs Python dependencies with:
26
+
27
+ ```bash
28
+ python -m pip install -e ".[dev]"
29
+ ```
30
+
31
+ It does not require `npm ci` because the package does not currently ship a
32
+ standalone lockfile. The release-smoke job validates the release manifest, runs
33
+ focused release-readiness tests, runs `python scripts/release.py verify`, and
34
+ checks npm launcher package shape with `npm pack --dry-run --json`.
35
+
36
+ The CI suite intentionally stays browser-free. It does not run live LinkedIn,
37
+ browser-profile, final-submit, or user-layer-file workflows.
38
+
39
+ ## Security
40
+
41
+ The `Security` workflow has three jobs:
42
+
43
+ - `codeql` runs committed CodeQL advanced setup for Python and JavaScript only,
44
+ with `security-extended` queries.
45
+ - `dependency-review` runs only on pull requests and fails on high or critical
46
+ dependency risk through `fail-on-severity: high`.
47
+ - `secret-scan` runs Gitleaks against the checked-out repository history.
48
+
49
+ Workflow permissions default to `contents: read`. The only write permission is
50
+ `security-events: write`, and it is limited to the CodeQL job.
51
+ The Gitleaks step receives the default `GITHUB_TOKEN` only for action API access
52
+ and sets `GITLEAKS_ENABLE_COMMENTS=false`, so it does not need pull-request write
53
+ permissions.
54
+
55
+ Phase 28 deliberately does not add Bandit, Semgrep, or another extra SAST tool.
56
+ Dependabot covers GitHub Actions, npm, and pip at repository root `/` with
57
+ weekly grouped updates, open pull request limit `5`, and no auto-merge. Labels
58
+ and assignees are skipped unless maintainers add a label policy later.
59
+
60
+ ## Release Automation Boundary
61
+
62
+ This project currently uses manual GitHub source releases. Phase 28 does not
63
+ enable Release Please, semantic-release, tag automation, changelog automation,
64
+ or any equivalent release writer.
65
+
66
+ Conventional Commits are recommended for maintainability, but CI does not
67
+ enforce commit-message format.
68
+
69
+ The workflows and Dependabot config are source-release metadata. They are kept
70
+ in `release-manifest.json` for source-checkout visibility, but workflow files
71
+ and `.github/dependabot.yml` are excluded from npm package contents unless a
72
+ future phase intentionally documents otherwise.
73
+
74
+ ## Deferred Provenance Work
75
+
76
+ The following controls are feasible future work, not active behavior:
77
+
78
+ - SBOM generation, after a real artifact channel is selected.
79
+ - Artifact attestations, after the project intentionally grants
80
+ `id-token: write` and `attestations: write`.
81
+ - Signing and immutable-release policy, after release assets or package
82
+ channels exist.
83
+ - Trusted publisher setup or package-name reservation, in a dedicated registry
84
+ publication phase.
85
+
86
+ The future registry channel order, trusted-publishing boundary, approval
87
+ templates, and rollback limits are documented in the
88
+ [registry publication strategy](registry-publication-strategy.md).
89
+
90
+ No Phase 28 workflow grants `packages: write`, `id-token: write`, or
91
+ `attestations: write`.
92
+
93
+ ## No-Surprise Publish Boundary
94
+
95
+ Phase 28 automation must not:
96
+
97
+ - run `npm publish`
98
+ - run `twine upload`
99
+ - create, edit, delete, or upload a GitHub Release
100
+ - create or push tags
101
+ - reserve package names
102
+ - configure trusted publishers
103
+ - mutate branch rulesets, tag rulesets, required checks, or repository settings
104
+
105
+ Public sync and live workflow verification remain explicit maintainer actions.