---ci---
project: acdl
phase: 5
milestone: v1.28
status: execute
persona: security-engineer
---
Add tests/test_e2e_idp.py — the J1+J2 happy-path E2E flow. Uses moto
for DynamoDB (4 IdP tables) + mock KMS (test ECC keypair). Asserts:
(a) sign_up succeeds, (b) sign_in returns a session, (c) token-vend
returns a KMS-signed OIDC token, (d) the OIDC token verifies with the
JWKS key (pyjwt), (e) nova apply --local produces a JWS attestation
(HS256), (f) the JWS verifies with the PAT-derived key (+ tamper
detection), (g) the audit chain is complete + linked (auth.sign_up,
auth.sign_in, auth.session_created, pat.issued, token.vend.allowed —
all present, linked by user_id/jti, no raw password/PAT leaked
INV-16). Also: the credentials file stores the OIDC token not the raw
PAT (C-7.3), the DDB user item has a password_hash not the raw
password, the DDB PAT row has a pat_hash not the raw PAT. Negative
path: revocation breaks the chain (403 pat_revoked, D-229 strong-read
SLO, token.vend.denied audit event).
The full nova-idp-auth Lambda handler is included in this commit (sign_up,
sign_in, create_session, request_password_reset, reset_password) since the
hashing module and handler share one file. The Argon2id hashing + fail-closed
logic is the security-engineer territory; the Lambda plumbing is backend-engineer.
---ci---
project: acdl
phase: 3
milestone: v1.28
status: execute
persona: security-engineer
---
---ci---
project: acdl
phase: 2
milestone: v1.28
status: execute
persona: backend-engineer
---
tests/test_init_attestations.py: nova init in a tmp_path creates
.nova/contract.yml.attestations/ as an empty directory (listdir == []).
The existing test_cli_subcommands.py asserts is_dir() but not emptiness;
this is the explicit REQ-331 assertion (freshly scaffolded repo has no
attestations yet — they are produced later by nova apply --sign-local-review
/ the JWS attestation flow, REQ-332).
---ci---
project: acdl
phase: 2
milestone: v1.28
status: execute
persona: backend-engineer
---
nova apply subcommand (44 lines, CAP-034: <=50 lines, <=3 functions, no if
except __main__ guard). --local calls core.env.synthesize_local_env() +
core.contract_resolver.resolve(). --sign-local-review calls
core.jws_attestation.sign_attestation() (REQ-332) and appends the JWS to the
output. Delegates to core/ — no business logic in the subcommand (NFR-7).
Auto-registered via nova/cli.py pkgutil discovery; CAP-033/034 tests pass.
---ci---
project: acdl
phase: 2
milestone: v1.28
status: execute
persona: backend-engineer
---
Extract dispatch_action() shared business-logic dispatch + _to_http_response
error mapper. lambda_handler (Lambda) + cli_main (CLI) become thin input
parsers that both delegate to dispatch_action. The action routing, contract
validation, DynamoDB write, error reporting live in shared functions — single
source of truth (NFR-7). tests/test_dual_use.py verifies both paths produce
the same output for the same input, both call dispatch_action, and code
share >=80% (CAP-026). 41 existing ingestor tests still pass.
tests/test_forge_action_byte_identical.py — 15 tests asserting the
structural invariants of the nova cli-action composite action
(.github/actions/nova-cli/action.yml). The action is consumed by both
the production forge + the dev forge via the same file path, so a
single source under test guarantees both platforms consume the same
bytes (the byte-identical requirement, NFR-11).
Structural invariants covered (the unit-testable subset):
(a) action.yml is valid YAML
(b) name present + non-empty
(c) inputs.command required: true
(d) inputs.contract / mode / version exist with documented defaults
(.nova/contract.yml, "", "latest") and are not required
(e) runs.using == "composite"
(f) a setup-python@v5 step pins python-version "3.12" (REQ-326 AC3)
(g) an install step installs `nova` via both CodeArtifact
(codeartifact login --tool pip) + fallback (--index-url) paths,
parameterised by inputs.version
(h) a run step executes `nova ${{ inputs.command }}` with
NOVA_CLIENT_MODE (from inputs.mode) + NOVA_CONTRACT (from
inputs.contract) env forwarded
NFR-11 byte-identical source guard: the action.yml must not embed
forge-specific hostnames / org names / the dev-forge or consumer-mirror
names, and the install path must be selected by env var at runtime
(NOT a forge-identity conditional) — so the file stays byte-identical
across forges. Both asserted.
The full byte-identical cross-platform verification (NFR-11,
REQ-326 AC2) — running the action with identical inputs on a
production-forge ubuntu-latest runner + a dev-forge act_runner and
asserting identical stdout + exit code — is a CI matrix job, not a
unit test. It cannot be reproduced in-process (depends on two external
runner environments). Documented in the module docstring + the
action.yml header; the CI matrix job is defined out-of-band.
All 15 tests pass. No regressions in tests/test_pipeline_contract.py,
tests/test_deploy_workflow_env_input.py, tests/test_rotate_key_workflow.py
(77 passed). tests/test_no_forge_mentions.py passes (the test file +
action.yml + publish.yml are clean of forge-specific strings).
---ci---
project: acdl
phase: 1
milestone: v1.28
status: execute
persona: backend-engineer
---/ci---
.github/actions/nova-cli/action.yml — composite action discovered by
both the production forge (GitHub Actions) and the dev forge
(act_runner) via the shared .github/actions/nova-cli/ path. No separate
dev-forge action file is needed; the same path works on both platforms.
Consumers reference it via a versioned tag pin:
uses: <org>/<repo>/.github/actions/nova-cli@v1.28
inputs:
- command (required) — the nova subcommand + args, passed verbatim to
`nova`
- contract (default .nova/contract.yml) — forwarded via NOVA_CONTRACT
- mode (default "") — forwarded via NOVA_CLIENT_MODE (agent /
interactive / plan-only / check-only); empty = let nova resolve
- version (default "latest") — pin to a released wheel version for
reproducible runs
runs.using: composite with 3 steps:
1. actions/setup-python@v5 with python-version "3.12" (REQ-326 AC3)
2. Install Nova (CodeArtifact default + fallback index):
- NOVA_CODEARTIFACT_DOMAIN set → aws codeartifact login --tool pip
--domain $DOMAIN --repository nova-pypi → pip install nova==<ver>
- else → pip install --index-url $NOVA_WHEEL_INDEX nova==<ver>
Fails closed if neither is configured.
3. Run Nova: `nova ${{ inputs.command }}` with NOVA_CLIENT_MODE +
NOVA_CONTRACT env from inputs.
NFR-11 byte-identical cross-platform verification is a CI matrix job
(production forge ubuntu-latest + dev forge act_runner with identical
inputs, assert same stdout + exit code) — not reproducible in a unit
test. Structural invariants are asserted by
tests/test_forge_action_byte_identical.py (next commit).
---ci---
project: acdl
phase: 1
milestone: v1.28
status: execute
persona: backend-engineer
---/ci---
CodeArtifact provisioning check in account 581513795199 could not
complete — no AWS credentials available in the P1 execute environment
("Unable to locate credentials"). Per the task spec, provisioning is NOT
attempted (requires codeartifact:* IAM grants not confirmed for the
execute principal). Documented as a P1 blocker for the CodeArtifact mode
of the publish workflow's wheel-upload step.
docs/codeartifact-provisioning.md records:
- (a) the attempted commands (list-domains, describe-repository,
list-repositories) + the credentials-not-found error
- (b) the required IAM grants for a follow-up provisioning task:
codeartifact:CreateDomain, CreateRepository, GetRepositoryEndpoint,
GetAuthorizationToken, ReadFromRepository, PublishPackageToRepository
+ ssm:PutParameter (CAP-035) + lambda:PublishLayerVersion
- (c) the fallback: a private wheel index selected at deploy time via
the NOVA_WHEEL_INDEX env var (consumers / composite action) and
TWINE_REPOSITORY_URL + TWINE_USERNAME + TWINE_PASSWORD (publish step).
The workflow supports both CodeArtifact mode (NOVA_CODEARTIFACT_DOMAIN
set) and fallback-index mode (unset) — no single hostname is baked
into the synced workflow files.
CAP-035 invariant (SSM /nova/layer/nova-cli/version = <wheel-version>:
<layer-arn>) is unaffected by the index choice and is recorded
atomically after both the wheel upload + layer publish succeed.
---ci---
project: acdl
phase: 1
milestone: v1.28
status: execute
persona: backend-engineer
---/ci---
CAP-033: `nova --help` exits 0 and lists a subcommand for every
user-facing core/ module (15 expected subcommands parsed from help).
CAP-034 (AST scan, parametrized per nova/<module>.py excl. cli/__init__):
- (a) line count ≤50
- (b) ≤3 FunctionDef/AsyncFunctionDef
- (c) every bare ast.Call target resolves to a core.* import, a builtin,
or a local function def (attribute/method calls allowed)
- (d) no `if` statements except `if __name__ == "__main__"`
nova init: in tmp_path, asserts .nova/, .nova/contract.yml.attestations/,
.gitignore created with all 6 secrets-exclusion lines; refuses existing
dir without --force.
---ci---
project: acdl
phase: 1
milestone: v1.28
status: execute
persona: cli-engineer
---/ci---